{"id":13394033,"url":"https://github.com/ant0ine/go-json-rest","last_synced_at":"2025-05-14T11:10:54.140Z","repository":{"id":7013138,"uuid":"8282363","full_name":"ant0ine/go-json-rest","owner":"ant0ine","description":"A quick and easy way to setup a RESTful JSON API","archived":false,"fork":false,"pushed_at":"2021-01-23T18:47:50.000Z","size":982,"stargazers_count":3512,"open_issues_count":46,"forks_count":382,"subscribers_count":154,"default_branch":"master","last_synced_at":"2025-04-11T04:55:31.733Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ant0ine.github.io/go-json-rest/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ant0ine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-02-19T03:15:45.000Z","updated_at":"2025-04-06T13:54:02.000Z","dependencies_parsed_at":"2022-09-26T22:10:14.683Z","dependency_job_id":null,"html_url":"https://github.com/ant0ine/go-json-rest","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ant0ine%2Fgo-json-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ant0ine%2Fgo-json-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ant0ine%2Fgo-json-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ant0ine%2Fgo-json-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ant0ine","download_url":"https://codeload.github.com/ant0ine/go-json-rest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129490,"owners_count":22019628,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:01:06.588Z","updated_at":"2025-05-14T11:10:54.093Z","avatar_url":"https://github.com/ant0ine.png","language":"Go","funding_links":[],"categories":["`API Frameworks`","Go","Web框架","Web Frameworks","Servers","API Frameworks","web框架","Utility","web框架`web 框架`"],"sub_categories":["Go","实用程序/Miscellaneous","Advanced Console UIs","Utility/Miscellaneous","HTTP Clients","Fail injection","版本控制","版本控制`版本控制相关库`","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","交流"],"readme":"\n# Go-Json-Rest\n\n*A quick and easy way to setup a RESTful JSON API*\n\n[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/ant0ine/go-json-rest/rest) [![license](https://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/ant0ine/go-json-rest/master/LICENSE) [![build](https://img.shields.io/travis/ant0ine/go-json-rest.svg?style=flat)](https://travis-ci.org/ant0ine/go-json-rest)\n\n\n**Go-Json-Rest** is a thin layer on top of `net/http` that helps building RESTful JSON APIs easily. It provides fast and scalable request routing using a Trie based implementation, helpers to deal with JSON requests and responses, and middlewares for functionalities like CORS, Auth, Gzip, Status ...\n\n\n## Table of content\n\n- [Features](#features)\n- [Install](#install)\n- [Vendoring](#vendoring)\n- [Middlewares](#middlewares)\n- [Examples](#examples)\n  - [Basics](#basics)\n\t  - [Hello World!](#hello-world)\n\t  - [Lookup](#lookup)\n\t  - [Countries](#countries)\n\t  - [Users](#users)\n  - [Applications](#applications)\n\t  - [API and static files](#api-and-static-files)\n\t  - [GORM](#gorm)\n\t  - [CORS](#cors)\n\t  - [JSONP](#jsonp)\n\t  - [Basic Auth](#basic-auth)\n\t  - [Force HTTPS](#forcessl)\n\t  - [Status](#status)\n\t  - [Status Auth](#status-auth)\n  - [Advanced](#advanced)\n\t  - [JWT](#jwt)\n\t  - [Streaming](#streaming)\n\t  - [Non JSON payload](#non-json-payload)\n\t  - [API Versioning](#api-versioning)\n\t  - [Statsd](#statsd)\n\t  - [NewRelic](#newrelic)\n\t  - [Graceful Shutdown](#graceful-shutdown)\n\t  - [SPDY](#spdy)\n\t  - [Google App Engine](#gae)\n\t  - [Websocket](#websocket)\n- [External Documentation](#external-documentation)\n- [Version 3 release notes](#version-3-release-notes)\n- [Migration guide from v2 to v3](#migration-guide-from-v2-to-v3)\n- [Version 2 release notes](#version-2-release-notes)\n- [Migration guide from v1 to v2](#migration-guide-from-v1-to-v2)\n- [Thanks](#thanks)\n\n\n## Features\n\n- Many examples.\n- Fast and scalable URL routing. It implements the classic route description syntax using a Trie data structure.\n- Architecture based on a router(App) sitting on top of a stack of Middlewares.\n- The Middlewares implement functionalities like Logging, Gzip, CORS, Auth, Status, ...\n- Implemented as a `net/http` Handler. This standard interface allows combinations with other Handlers.\n- Test package to help writing tests for your API.\n- Monitoring statistics inspired by Memcached.\n\n\n## Install\n\nThis package is \"go-gettable\", just do:\n\n    go get github.com/ant0ine/go-json-rest/rest\n\n\n## Vendoring\n\nThe recommended way of using this library in your project is to use the **\"vendoring\"** method,\nwhere this library code is copied in your repository at a specific revision.\n[This page](https://nathany.com/go-packages/) is a good summary of package management in Go.\n\n\n## Middlewares\n\nCore Middlewares:\n\n| Name | Description |\n|------|-------------|\n| **AccessLogApache** | Access log inspired by Apache mod_log_config |\n| **AccessLogJson** | Access log with records as JSON |\n| **AuthBasic** | Basic HTTP auth |\n| **ContentTypeChecker** | Verify the request content type |\n| **Cors** | CORS server side implementation |\n| **Gzip** | Compress the responses |\n| **If** | Conditionally execute a Middleware at runtime |\n| **JsonIndent** | Easy to read JSON |\n| **Jsonp** | Response as JSONP |\n| **PoweredBy** | Manage the X-Powered-By response header |\n| **Recorder** | Record the status code and content length in the Env |\n| **Status** | Memecached inspired stats about the requests |\n| **Timer** | Keep track of the elapsed time in the Env |\n\nThird Party Middlewares:\n\n| Name | Description |\n|------|-------------|\n| **[Statsd](https://github.com/ant0ine/go-json-rest-middleware-statsd)** | Send stats to a statsd server |\n| **[JWT](https://github.com/StephanDollberg/go-json-rest-middleware-jwt)** | Provides authentication via Json Web Tokens |\n| **[AuthToken](https://github.com/grayj/go-json-rest-middleware-tokenauth)** | Provides a Token Auth implementation |\n| **[ForceSSL](https://github.com/jadengore/go-json-rest-middleware-force-ssl)** | Forces SSL on requests |\n| **[SecureRedirect](https://github.com/clyphub/go-json-rest-middleware)** | Redirect clients from HTTP to HTTPS |\n\n*If you have a Go-Json-Rest compatible middleware, feel free to submit a PR to add it in this list, and in the examples.*\n\n\n## Examples\n\nAll the following examples can be found in dedicated examples repository: https://github.com/ant0ine/go-json-rest-examples\n\n### Basics\n\nFirst examples to try, as an introduction to go-json-rest.\n\n#### Hello World!\n\nTradition!\n\ncurl demo:\n``` sh\ncurl -i http://127.0.0.1:8080/\n```\n\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t}))\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Lookup\n\nDemonstrate how to use the relaxed placeholder (notation `#paramName`).\nThis placeholder matches everything until the first `/`, including `.`\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/lookup/google.com\ncurl -i http://127.0.0.1:8080/lookup/notadomain\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/lookup/#host\", func(w rest.ResponseWriter, req *rest.Request) {\n\t\t\tip, err := net.LookupIP(req.PathParam(\"host\"))\n\t\t\tif err != nil {\n\t\t\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.WriteJson(\u0026ip)\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Countries\n\nDemonstrate simple POST GET and DELETE operations\n\ncurl demo:\n```\ncurl -i -H 'Content-Type: application/json' \\\n    -d '{\"Code\":\"FR\",\"Name\":\"France\"}' http://127.0.0.1:8080/countries\ncurl -i -H 'Content-Type: application/json' \\\n    -d '{\"Code\":\"US\",\"Name\":\"United States\"}' http://127.0.0.1:8080/countries\ncurl -i http://127.0.0.1:8080/countries/FR\ncurl -i http://127.0.0.1:8080/countries/US\ncurl -i http://127.0.0.1:8080/countries\ncurl -i -X DELETE http://127.0.0.1:8080/countries/FR\ncurl -i http://127.0.0.1:8080/countries\ncurl -i -X DELETE http://127.0.0.1:8080/countries/US\ncurl -i http://127.0.0.1:8080/countries\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n\t\"sync\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/countries\", GetAllCountries),\n\t\trest.Post(\"/countries\", PostCountry),\n\t\trest.Get(\"/countries/:code\", GetCountry),\n\t\trest.Delete(\"/countries/:code\", DeleteCountry),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype Country struct {\n\tCode string\n\tName string\n}\n\nvar store = map[string]*Country{}\n\nvar lock = sync.RWMutex{}\n\nfunc GetCountry(w rest.ResponseWriter, r *rest.Request) {\n\tcode := r.PathParam(\"code\")\n\n\tlock.RLock()\n\tvar country *Country\n\tif store[code] != nil {\n\t\tcountry = \u0026Country{}\n\t\t*country = *store[code]\n\t}\n\tlock.RUnlock()\n\n\tif country == nil {\n\t\trest.NotFound(w, r)\n\t\treturn\n\t}\n\tw.WriteJson(country)\n}\n\nfunc GetAllCountries(w rest.ResponseWriter, r *rest.Request) {\n\tlock.RLock()\n\tcountries := make([]Country, len(store))\n\ti := 0\n\tfor _, country := range store {\n\t\tcountries[i] = *country\n\t\ti++\n\t}\n\tlock.RUnlock()\n\tw.WriteJson(\u0026countries)\n}\n\nfunc PostCountry(w rest.ResponseWriter, r *rest.Request) {\n\tcountry := Country{}\n\terr := r.DecodeJsonPayload(\u0026country)\n\tif err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif country.Code == \"\" {\n\t\trest.Error(w, \"country code required\", 400)\n\t\treturn\n\t}\n\tif country.Name == \"\" {\n\t\trest.Error(w, \"country name required\", 400)\n\t\treturn\n\t}\n\tlock.Lock()\n\tstore[country.Code] = \u0026country\n\tlock.Unlock()\n\tw.WriteJson(\u0026country)\n}\n\nfunc DeleteCountry(w rest.ResponseWriter, r *rest.Request) {\n\tcode := r.PathParam(\"code\")\n\tlock.Lock()\n\tdelete(store, code)\n\tlock.Unlock()\n\tw.WriteHeader(http.StatusOK)\n}\n\n```\n\n#### Users\n\nDemonstrate how to use Method Values.\n\nMethod Values have been [introduced in Go 1.1](https://golang.org/doc/go1.1#method_values).\n\nThis shows how to map a Route to a method of an instantiated object (i.e: receiver of the method)\n\ncurl demo:\n```\ncurl -i -H 'Content-Type: application/json' \\\n    -d '{\"Name\":\"Antoine\"}' http://127.0.0.1:8080/users\ncurl -i http://127.0.0.1:8080/users/0\ncurl -i -X PUT -H 'Content-Type: application/json' \\\n    -d '{\"Name\":\"Antoine Imbert\"}' http://127.0.0.1:8080/users/0\ncurl -i -X DELETE http://127.0.0.1:8080/users/0\ncurl -i http://127.0.0.1:8080/users\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n\t\"sync\"\n)\n\nfunc main() {\n\n\tusers := Users{\n\t\tStore: map[string]*User{},\n\t}\n\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/users\", users.GetAllUsers),\n\t\trest.Post(\"/users\", users.PostUser),\n\t\trest.Get(\"/users/:id\", users.GetUser),\n\t\trest.Put(\"/users/:id\", users.PutUser),\n\t\trest.Delete(\"/users/:id\", users.DeleteUser),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype User struct {\n\tId   string\n\tName string\n}\n\ntype Users struct {\n\tsync.RWMutex\n\tStore map[string]*User\n}\n\nfunc (u *Users) GetAllUsers(w rest.ResponseWriter, r *rest.Request) {\n\tu.RLock()\n\tusers := make([]User, len(u.Store))\n\ti := 0\n\tfor _, user := range u.Store {\n\t\tusers[i] = *user\n\t\ti++\n\t}\n\tu.RUnlock()\n\tw.WriteJson(\u0026users)\n}\n\nfunc (u *Users) GetUser(w rest.ResponseWriter, r *rest.Request) {\n\tid := r.PathParam(\"id\")\n\tu.RLock()\n\tvar user *User\n\tif u.Store[id] != nil {\n\t\tuser = \u0026User{}\n\t\t*user = *u.Store[id]\n\t}\n\tu.RUnlock()\n\tif user == nil {\n\t\trest.NotFound(w, r)\n\t\treturn\n\t}\n\tw.WriteJson(user)\n}\n\nfunc (u *Users) PostUser(w rest.ResponseWriter, r *rest.Request) {\n\tuser := User{}\n\terr := r.DecodeJsonPayload(\u0026user)\n\tif err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tu.Lock()\n\tid := fmt.Sprintf(\"%d\", len(u.Store)) // stupid\n\tuser.Id = id\n\tu.Store[id] = \u0026user\n\tu.Unlock()\n\tw.WriteJson(\u0026user)\n}\n\nfunc (u *Users) PutUser(w rest.ResponseWriter, r *rest.Request) {\n\tid := r.PathParam(\"id\")\n\tu.Lock()\n\tif u.Store[id] == nil {\n\t\trest.NotFound(w, r)\n\t\tu.Unlock()\n\t\treturn\n\t}\n\tuser := User{}\n\terr := r.DecodeJsonPayload(\u0026user)\n\tif err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\tu.Unlock()\n\t\treturn\n\t}\n\tuser.Id = id\n\tu.Store[id] = \u0026user\n\tu.Unlock()\n\tw.WriteJson(\u0026user)\n}\n\nfunc (u *Users) DeleteUser(w rest.ResponseWriter, r *rest.Request) {\n\tid := r.PathParam(\"id\")\n\tu.Lock()\n\tdelete(u.Store, id)\n\tu.Unlock()\n\tw.WriteHeader(http.StatusOK)\n}\n\n```\n\n\n### Applications\n\nCommon use cases, found in many applications.\n\n#### API and static files\n\nCombine Go-Json-Rest with other handlers.\n\n`api.MakeHandler()` is a valid `http.Handler`, and can be combined with other handlers.\nIn this example the api handler is used under the `/api/` prefix, while a FileServer is instantiated under the `/static/` prefix.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/api/message\ncurl -i http://127.0.0.1:8080/static/main.go\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/message\", func(w rest.ResponseWriter, req *rest.Request) {\n\t\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\n\thttp.Handle(\"/api/\", http.StripPrefix(\"/api\", api.MakeHandler()))\n\n\thttp.Handle(\"/static/\", http.StripPrefix(\"/static\", http.FileServer(http.Dir(\".\"))))\n\n\tlog.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n\n```\n\n#### GORM\n\nDemonstrate basic CRUD operation using a store based on MySQL and GORM\n\n[GORM](https://github.com/jinzhu/gorm) is simple ORM library for Go.\nIn this example the same struct is used both as the GORM model and as the JSON model.\n\ncurl demo:\n```\ncurl -i -H 'Content-Type: application/json' \\\n    -d '{\"Message\":\"this is a test\"}' http://127.0.0.1:8080/reminders\ncurl -i http://127.0.0.1:8080/reminders/1\ncurl -i http://127.0.0.1:8080/reminders\ncurl -i -X PUT -H 'Content-Type: application/json' \\\n    -d '{\"Message\":\"is updated\"}' http://127.0.0.1:8080/reminders/1\ncurl -i -X DELETE http://127.0.0.1:8080/reminders/1\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jinzhu/gorm\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\n\ti := Impl{}\n\ti.InitDB()\n\ti.InitSchema()\n\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/reminders\", i.GetAllReminders),\n\t\trest.Post(\"/reminders\", i.PostReminder),\n\t\trest.Get(\"/reminders/:id\", i.GetReminder),\n\t\trest.Put(\"/reminders/:id\", i.PutReminder),\n\t\trest.Delete(\"/reminders/:id\", i.DeleteReminder),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype Reminder struct {\n\tId        int64     `json:\"id\"`\n\tMessage   string    `sql:\"size:1024\" json:\"message\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n\tDeletedAt time.Time `json:\"-\"`\n}\n\ntype Impl struct {\n\tDB *gorm.DB\n}\n\nfunc (i *Impl) InitDB() {\n\tvar err error\n\ti.DB, err = gorm.Open(\"mysql\", \"gorm:gorm@/gorm?charset=utf8\u0026parseTime=True\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Got error when connect database, the error is '%v'\", err)\n\t}\n\ti.DB.LogMode(true)\n}\n\nfunc (i *Impl) InitSchema() {\n\ti.DB.AutoMigrate(\u0026Reminder{})\n}\n\nfunc (i *Impl) GetAllReminders(w rest.ResponseWriter, r *rest.Request) {\n\treminders := []Reminder{}\n\ti.DB.Find(\u0026reminders)\n\tw.WriteJson(\u0026reminders)\n}\n\nfunc (i *Impl) GetReminder(w rest.ResponseWriter, r *rest.Request) {\n\tid := r.PathParam(\"id\")\n\treminder := Reminder{}\n\tif i.DB.First(\u0026reminder, id).Error != nil {\n\t\trest.NotFound(w, r)\n\t\treturn\n\t}\n\tw.WriteJson(\u0026reminder)\n}\n\nfunc (i *Impl) PostReminder(w rest.ResponseWriter, r *rest.Request) {\n\treminder := Reminder{}\n\tif err := r.DecodeJsonPayload(\u0026reminder); err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif err := i.DB.Save(\u0026reminder).Error; err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tw.WriteJson(\u0026reminder)\n}\n\nfunc (i *Impl) PutReminder(w rest.ResponseWriter, r *rest.Request) {\n\n\tid := r.PathParam(\"id\")\n\treminder := Reminder{}\n\tif i.DB.First(\u0026reminder, id).Error != nil {\n\t\trest.NotFound(w, r)\n\t\treturn\n\t}\n\n\tupdated := Reminder{}\n\tif err := r.DecodeJsonPayload(\u0026updated); err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\treminder.Message = updated.Message\n\n\tif err := i.DB.Save(\u0026reminder).Error; err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tw.WriteJson(\u0026reminder)\n}\n\nfunc (i *Impl) DeleteReminder(w rest.ResponseWriter, r *rest.Request) {\n\tid := r.PathParam(\"id\")\n\treminder := Reminder{}\n\tif i.DB.First(\u0026reminder, id).Error != nil {\n\t\trest.NotFound(w, r)\n\t\treturn\n\t}\n\tif err := i.DB.Delete(\u0026reminder).Error; err != nil {\n\t\trest.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tw.WriteHeader(http.StatusOK)\n}\n\n```\n\n#### CORS\n\nDemonstrate how to setup CorsMiddleware around all the API endpoints.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/countries\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.Use(\u0026rest.CorsMiddleware{\n\t\tRejectNonCorsRequests: false,\n\t\tOriginValidator: func(origin string, request *rest.Request) bool {\n\t\t\treturn origin == \"http://my.other.host\"\n\t\t},\n\t\tAllowedMethods: []string{\"GET\", \"POST\", \"PUT\"},\n\t\tAllowedHeaders: []string{\n\t\t\t\"Accept\", \"Content-Type\", \"X-Custom-Header\", \"Origin\"},\n\t\tAccessControlAllowCredentials: true,\n\t\tAccessControlMaxAge:           3600,\n\t})\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/countries\", GetAllCountries),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype Country struct {\n\tCode string\n\tName string\n}\n\nfunc GetAllCountries(w rest.ResponseWriter, r *rest.Request) {\n\tw.WriteJson(\n\t\t[]Country{\n\t\t\tCountry{\n\t\t\t\tCode: \"FR\",\n\t\t\t\tName: \"France\",\n\t\t\t},\n\t\t\tCountry{\n\t\t\t\tCode: \"US\",\n\t\t\t\tName: \"United States\",\n\t\t\t},\n\t\t},\n\t)\n}\n\n```\n\n#### JSONP\n\nDemonstrate how to use the JSONP middleware.\n\ncurl demo:\n``` sh\ncurl -i http://127.0.0.1:8080/\ncurl -i http://127.0.0.1:8080/?cb=parseResponse\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.Use(\u0026rest.JsonpMiddleware{\n\t\tCallbackNameKey: \"cb\",\n\t})\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t}))\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Basic Auth\n\nDemonstrate how to setup AuthBasicMiddleware as a pre-routing middleware.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/\ncurl -i -u admin:admin http://127.0.0.1:8080/\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.Use(\u0026rest.AuthBasicMiddleware{\n\t\tRealm: \"test zone\",\n\t\tAuthenticator: func(userId string, password string) bool {\n\t\t\tif userId == \"admin\" \u0026\u0026 password == \"admin\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t})\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t}))\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### ForceSSL\n\nDemonstrate how to use the [ForceSSL Middleware](https://github.com/jadengore/go-json-rest-middleware-force-ssl) to force HTTPS on requests to a `go-json-rest` API.\n\nFor the purposes of this demo, we are using HTTP for all requests and checking the `X-Forwarded-Proto` header to see if it is set to HTTPS (many routers set this to show what type of connection the client is using, such as Heroku). To do a true HTTPS test, make sure and use [`http.ListenAndServeTLS`](https://golang.org/pkg/net/http/#ListenAndServeTLS) with a valid certificate and key file.\n\nAdditional documentation for the ForceSSL middleware can be found [here](https://github.com/jadengore/go-json-rest-middleware-force-ssl).\n\ncurl demo:\n``` sh\ncurl -i 127.0.0.1:8080/\ncurl -H \"X-Forwarded-Proto:https\" -i 127.0.0.1:8080/\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"github.com/jadengore/go-json-rest-middleware-force-ssl\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(\u0026forceSSL.Middleware{\n\t\tTrustXFPHeader:     true,\n\t\tEnable301Redirects: false,\n\t})\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n\t\tw.WriteJson(map[string]string{\"body\": \"Hello World!\"})\n\t}))\n\n\t// For the purposes of this demo, only HTTP connections accepted.\n\t// For true HTTPS, use ListenAndServeTLS.\n\t// https://golang.org/pkg/net/http/#ListenAndServeTLS\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Status\n\nDemonstrate how to setup a `/.status` endpoint\n\nInspired by memcached \"stats\", this optional feature can be enabled to help monitoring the service.\nThis example shows how to enable the stats, and how to setup the `/.status` route.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/.status\ncurl -i http://127.0.0.1:8080/.status\n...\n```\n\nOutput example:\n```\n{\n  \"Pid\": 21732,\n  \"UpTime\": \"1m15.926272s\",\n  \"UpTimeSec\": 75.926272,\n  \"Time\": \"2013-03-04 08:00:27.152986 +0000 UTC\",\n  \"TimeUnix\": 1362384027,\n  \"StatusCodeCount\": {\n        \"200\": 53,\n        \"404\": 11\n  },\n  \"TotalCount\": 64,\n  \"TotalResponseTime\": \"16.777ms\",\n  \"TotalResponseTimeSec\": 0.016777,\n  \"AverageResponseTime\": \"262.14us\",\n  \"AverageResponseTimeSec\": 0.00026214\n}\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tstatusMw := \u0026rest.StatusMiddleware{}\n\tapi.Use(statusMw)\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/.status\", func(w rest.ResponseWriter, r *rest.Request) {\n\t\t\tw.WriteJson(statusMw.GetStatus())\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Status Auth\n\nDemonstrate how to setup a /.status endpoint protected with basic authentication.\n\nThis is a good use case of middleware applied to only one API endpoint.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/countries\ncurl -i http://127.0.0.1:8080/.status\ncurl -i -u admin:admin http://127.0.0.1:8080/.status\n...\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tstatusMw := \u0026rest.StatusMiddleware{}\n\tapi.Use(statusMw)\n\tapi.Use(rest.DefaultDevStack...)\n\tauth := \u0026rest.AuthBasicMiddleware{\n\t\tRealm: \"test zone\",\n\t\tAuthenticator: func(userId string, password string) bool {\n\t\t\tif userId == \"admin\" \u0026\u0026 password == \"admin\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t}\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/countries\", GetAllCountries),\n\t\trest.Get(\"/.status\", auth.MiddlewareFunc(\n\t\t\tfunc(w rest.ResponseWriter, r *rest.Request) {\n\t\t\t\tw.WriteJson(statusMw.GetStatus())\n\t\t\t},\n\t\t)),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype Country struct {\n\tCode string\n\tName string\n}\n\nfunc GetAllCountries(w rest.ResponseWriter, r *rest.Request) {\n\tw.WriteJson(\n\t\t[]Country{\n\t\t\tCountry{\n\t\t\t\tCode: \"FR\",\n\t\t\t\tName: \"France\",\n\t\t\t},\n\t\t\tCountry{\n\t\t\t\tCode: \"US\",\n\t\t\t\tName: \"United States\",\n\t\t\t},\n\t\t},\n\t)\n}\n\n```\n\n\n### Advanced\n\nMore advanced use cases.\n\n#### JWT\n\nDemonstrates how to use the [Json Web Token Auth Middleware](https://github.com/StephanDollberg/go-json-rest-middleware-jwt) to authenticate via a JWT token.\n\ncurl demo:\n``` sh\ncurl -d '{\"username\": \"admin\", \"password\": \"admin\"}' -H \"Content-Type:application/json\" http://localhost:8080/api/login\ncurl -H \"Authorization:Bearer TOKEN_RETURNED_FROM_ABOVE\" http://localhost:8080/api/auth_test\ncurl -H \"Authorization:Bearer TOKEN_RETURNED_FROM_ABOVE\" http://localhost:8080/api/refresh_token\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/StephanDollberg/go-json-rest-middleware-jwt\"\n\t\"github.com/ant0ine/go-json-rest/rest\"\n)\n\nfunc handle_auth(w rest.ResponseWriter, r *rest.Request) {\n\tw.WriteJson(map[string]string{\"authed\": r.Env[\"REMOTE_USER\"].(string)})\n}\n\nfunc main() {\n\tjwt_middleware := \u0026jwt.JWTMiddleware{\n\t\tKey:        []byte(\"secret key\"),\n\t\tRealm:      \"jwt auth\",\n\t\tTimeout:    time.Hour,\n\t\tMaxRefresh: time.Hour * 24,\n\t\tAuthenticator: func(userId string, password string) bool {\n\t\t\treturn userId == \"admin\" \u0026\u0026 password == \"admin\"\n\t\t}}\n\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\t// we use the IfMiddleware to remove certain paths from needing authentication\n\tapi.Use(\u0026rest.IfMiddleware{\n\t\tCondition: func(request *rest.Request) bool {\n\t\t\treturn request.URL.Path != \"/login\"\n\t\t},\n\t\tIfTrue: jwt_middleware,\n\t})\n\tapi_router, _ := rest.MakeRouter(\n\t\trest.Post(\"/login\", jwt_middleware.LoginHandler),\n\t\trest.Get(\"/auth_test\", handle_auth),\n\t\trest.Get(\"/refresh_token\", jwt_middleware.RefreshHandler),\n\t)\n\tapi.SetApp(api_router)\n\n\thttp.Handle(\"/api/\", http.StripPrefix(\"/api\", api.MakeHandler()))\n\n\tlog.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n\n```\n\n#### Streaming\n\nDemonstrate a streaming REST API, where the data is \"flushed\" to the client ASAP.\n\nThe stream format is a Line Delimited JSON.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/stream\n```\n\nOutput:\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\nDate: Sun, 16 Feb 2014 00:39:19 GMT\nTransfer-Encoding: chunked\n\n{\"Name\":\"thing #1\"}\n{\"Name\":\"thing #2\"}\n{\"Name\":\"thing #3\"}\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(\u0026rest.AccessLogApacheMiddleware{})\n\tapi.Use(rest.DefaultCommonStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/stream\", StreamThings),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\ntype Thing struct {\n\tName string\n}\n\nfunc StreamThings(w rest.ResponseWriter, r *rest.Request) {\n\tcpt := 0\n\tfor {\n\t\tcpt++\n\t\tw.WriteJson(\n\t\t\t\u0026Thing{\n\t\t\t\tName: fmt.Sprintf(\"thing #%d\", cpt),\n\t\t\t},\n\t\t)\n\t\tw.(http.ResponseWriter).Write([]byte(\"\\n\"))\n\t\t// Flush the buffer to client\n\t\tw.(http.Flusher).Flush()\n\t\t// wait 3 seconds\n\t\ttime.Sleep(time.Duration(3) * time.Second)\n\t}\n}\n\n```\n\n#### Non JSON payload\n\nExceptional use of non JSON payloads.\n\nThe ResponseWriter implementation provided by go-json-rest is designed\nto build JSON responses. In order to serve different kind of content,\nit is recommended to either:\na) use another server and configure CORS\n   (see the cors/ example)\nb) combine the api.MakeHandler() with another http.Handler\n   (see api-and-static/ example)\n\nThat been said, exceptionally, it can be convenient to return a\ndifferent content type on a JSON endpoint. In this case, setting the\nContent-Type and using the type assertion to access the Write method\nis enough. As shown in this example.\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/message.txt\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/message.txt\", func(w rest.ResponseWriter, req *rest.Request) {\n\t\t\tw.Header().Set(\"Content-Type\", \"text/plain\")\n\t\t\tw.(http.ResponseWriter).Write([]byte(\"Hello World!\"))\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### API Versioning\n\nFirst, API versioning is not easy and you may want to favor a mechanism that uses only backward compatible changes and deprecation cycles.\n\nThat been said, here is an example of API versioning using [Semver](http://semver.org/)\n\nIt defines a middleware that parses the version, checks a min and a max, and makes it available in the `request.Env`.\n\ncurl demo:\n``` sh\ncurl -i http://127.0.0.1:8080/api/1.0.0/message\ncurl -i http://127.0.0.1:8080/api/2.0.0/message\ncurl -i http://127.0.0.1:8080/api/2.0.1/message\ncurl -i http://127.0.0.1:8080/api/0.0.1/message\ncurl -i http://127.0.0.1:8080/api/4.0.1/message\n\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"github.com/coreos/go-semver/semver\"\n\t\"log\"\n\t\"net/http\"\n)\n\ntype SemVerMiddleware struct {\n\tMinVersion string\n\tMaxVersion string\n}\n\nfunc (mw *SemVerMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc {\n\n\tminVersion, err := semver.NewVersion(mw.MinVersion)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tmaxVersion, err := semver.NewVersion(mw.MaxVersion)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn func(writer rest.ResponseWriter, request *rest.Request) {\n\n\t\tversion, err := semver.NewVersion(request.PathParam(\"version\"))\n\t\tif err != nil {\n\t\t\trest.Error(\n\t\t\t\twriter,\n\t\t\t\t\"Invalid version: \"+err.Error(),\n\t\t\t\thttp.StatusBadRequest,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tif version.LessThan(*minVersion) {\n\t\t\trest.Error(\n\t\t\t\twriter,\n\t\t\t\t\"Min supported version is \"+minVersion.String(),\n\t\t\t\thttp.StatusBadRequest,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tif maxVersion.LessThan(*version) {\n\t\t\trest.Error(\n\t\t\t\twriter,\n\t\t\t\t\"Max supported version is \"+maxVersion.String(),\n\t\t\t\thttp.StatusBadRequest,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\trequest.Env[\"VERSION\"] = version\n\t\thandler(writer, request)\n\t}\n}\n\nfunc main() {\n\n\tsvmw := SemVerMiddleware{\n\t\tMinVersion: \"1.0.0\",\n\t\tMaxVersion: \"3.0.0\",\n\t}\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/#version/message\", svmw.MiddlewareFunc(\n\t\t\tfunc(w rest.ResponseWriter, req *rest.Request) {\n\t\t\t\tversion := req.Env[\"VERSION\"].(*semver.Version)\n\t\t\t\tif version.Major == 2 {\n\t\t\t\t\t// https://en.wikipedia.org/wiki/Second-system_effect\n\t\t\t\t\tw.WriteJson(map[string]string{\n\t\t\t\t\t\t\"Body\": \"Hello broken World!\",\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tw.WriteJson(map[string]string{\n\t\t\t\t\t\t\"Body\": \"Hello World!\",\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t},\n\t\t)),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\thttp.Handle(\"/api/\", http.StripPrefix(\"/api\", api.MakeHandler()))\n\tlog.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n\n```\n\n#### Statsd\n\nDemonstrate how to use the [Statsd Middleware](https://github.com/ant0ine/go-json-rest-middleware-statsd) to collect statistics about the requests/reponses.\nThis middleware is based on the [g2s](https://github.com/peterbourgon/g2s) statsd client.\n\ncurl demo:\n``` sh\n# start statsd server\n# monitor network\nngrep -d any port 8125\n\ncurl -i http://127.0.0.1:8080/message\ncurl -i http://127.0.0.1:8080/doesnotexist\n\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest-middleware-statsd\"\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(\u0026statsd.StatsdMiddleware{})\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, req *rest.Request) {\n\n\t\t// take more than 1ms so statsd can report it\n\t\ttime.Sleep(100 * time.Millisecond)\n\n\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t}))\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### NewRelic\n\nNewRelic integration based on the GoRelic plugin: [github.com/yvasiyarov/gorelic](https://github.com/yvasiyarov/gorelic)\n\ncurl demo:\n``` sh\ncurl -i http://127.0.0.1:8080/\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"github.com/yvasiyarov/go-metrics\"\n\t\"github.com/yvasiyarov/gorelic\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\ntype NewRelicMiddleware struct {\n\tLicense string\n\tName    string\n\tVerbose bool\n\tagent   *gorelic.Agent\n}\n\nfunc (mw *NewRelicMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc {\n\n\tmw.agent = gorelic.NewAgent()\n\tmw.agent.NewrelicLicense = mw.License\n\tmw.agent.HTTPTimer = metrics.NewTimer()\n\tmw.agent.Verbose = mw.Verbose\n\tmw.agent.NewrelicName = mw.Name\n\tmw.agent.CollectHTTPStat = true\n\tmw.agent.Run()\n\n\treturn func(writer rest.ResponseWriter, request *rest.Request) {\n\n\t\thandler(writer, request)\n\n\t\t// the timer middleware keeps track of the time\n\t\tstartTime := request.Env[\"START_TIME\"].(*time.Time)\n\t\tmw.agent.HTTPTimer.UpdateSince(*startTime)\n\t}\n}\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.Use(\u0026NewRelicMiddleware{\n\t\tLicense: \"\u003cREPLACE WITH THE LICENSE KEY\u003e\",\n\t\tName:    \"\u003cREPLACE WITH THE APP NAME\u003e\",\n\t\tVerbose: true,\n\t})\n\tapi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t}))\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### Graceful Shutdown\n\nThis example uses [https://github.com/tylerb/graceful](https://github.com/tylerb/graceful) to try to be nice with the clients waiting for responses during a server shutdown (or restart).\nThe HTTP response takes 10 seconds to be completed, printing a message on the wire every second.\n10 seconds is also the timeout set for the graceful shutdown.\nYou can play with these numbers to show that the server waits for the responses to complete.\n\ncurl demo:\n``` sh\ncurl -i http://127.0.0.1:8080/message\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/ant0ine/go-json-rest/rest\"\n        \"gopkg.in/tylerb/graceful.v1\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/message\", func(w rest.ResponseWriter, req *rest.Request) {\n\t\t\tfor cpt := 1; cpt \u003c= 10; cpt++ {\n\n\t\t\t\t// wait 1 second\n\t\t\t\ttime.Sleep(time.Duration(1) * time.Second)\n\n\t\t\t\tw.WriteJson(map[string]string{\n\t\t\t\t\t\"Message\": fmt.Sprintf(\"%d seconds\", cpt),\n\t\t\t\t})\n\t\t\t\tw.(http.ResponseWriter).Write([]byte(\"\\n\"))\n\n\t\t\t\t// Flush the buffer to client\n\t\t\t\tw.(http.Flusher).Flush()\n\t\t\t}\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\n\tserver := \u0026graceful.Server{\n\t\tTimeout: 10 * time.Second,\n\t\tServer: \u0026http.Server{\n\t\t\tAddr:    \":8080\",\n\t\t\tHandler: api.MakeHandler(),\n\t\t},\n\t}\n\n\tlog.Fatal(server.ListenAndServe())\n}\n\n```\n\n#### SPDY\n\nDemonstrate how to use SPDY with https://github.com/shykes/spdy-go\n\nFor a command line client, install spdycat from:\nhttps://github.com/tatsuhiro-t/spdylay\n\nspdycat demo:\n```\nspdycat -v --no-tls -2 http://localhost:8080/users/0\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"github.com/shykes/spdy-go\"\n\t\"log\"\n)\n\ntype User struct {\n\tId   string\n\tName string\n}\n\nfunc GetUser(w rest.ResponseWriter, req *rest.Request) {\n\tuser := User{\n\t\tId:   req.PathParam(\"id\"),\n\t\tName: \"Antoine\",\n\t}\n\tw.WriteJson(\u0026user)\n}\n\nfunc main() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/users/:id\", GetUser),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\tlog.Fatal(spdy.ListenAndServeTCP(\":8080\", api.MakeHandler()))\n}\n\n```\n\n#### GAE\n\nDemonstrate a simple Google App Engine app\n\nHere are my steps to make it work with the GAE SDK.\n(Probably not the best ones)\n\nAssuming that go-json-rest is installed using \"go get\"\nand that the GAE SDK is also installed.\n\nSetup:\n * copy this examples/gae/ dir outside of the go-json-rest/ tree\n * cd gae/\n * mkdir -p github.com/ant0ine\n * cp -r $GOPATH/src/github.com/ant0ine/go-json-rest github.com/ant0ine/go-json-rest\n * rm -rf github.com/ant0ine/go-json-rest/examples/\n * path/to/google_appengine/dev_appserver.py .\n\ncurl demo:\n```\ncurl -i http://127.0.0.1:8080/message\n```\n\ncode:\n``` go\npackage gaehelloworld\n\nimport (\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc init() {\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\trouter, err := rest.MakeRouter(\n\t\t\u0026rest.Get(\"/message\", func(w rest.ResponseWriter, req *rest.Request) {\n\t\t\tw.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tapi.SetApp(router)\n\thttp.Handle(\"/\", api.MakeHandler())\n}\n\n```\n\n#### Websocket\n\nDemonstrate how to run websocket in go-json-rest\n\ngo client demo:\n```go\norigin := \"http://localhost:8080/\"\nurl := \"ws://localhost:8080/ws\"\nws, err := websocket.Dial(url, \"\", origin)\nif err != nil {\n\tlog.Fatal(err)\n}\nif _, err := ws.Write([]byte(\"hello, world\\n\")); err != nil {\n\tlog.Fatal(err)\n}\nvar msg = make([]byte, 512)\nvar n int\nif n, err = ws.Read(msg); err != nil {\n\tlog.Fatal(err)\n}\nlog.Printf(\"Received: %s.\", msg[:n])\n```\n\ncode:\n``` go\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/ant0ine/go-json-rest/rest\"\n\t\"golang.org/x/net/websocket\"\n)\n\nfunc main() {\n\twsHandler := websocket.Handler(func(ws *websocket.Conn) {\n\t\tio.Copy(ws, ws)\n\t})\n\n\trouter, err := rest.MakeRouter(\n\t\trest.Get(\"/ws\", func(w rest.ResponseWriter, r *rest.Request) {\n\t\t\twsHandler.ServeHTTP(w.(http.ResponseWriter), r.Request)\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tapi := rest.NewApi()\n\tapi.Use(rest.DefaultDevStack...)\n\tapi.SetApp(router)\n\tlog.Fatal(http.ListenAndServe(\":8080\", api.MakeHandler()))\n}\n\n```\n\n\n\n## External Documentation\n\n- [Online Documentation (godoc.org)](https://godoc.org/github.com/ant0ine/go-json-rest/rest)\n\nOld v1 blog posts:\n\n- [(Blog Post) Introducing Go-Json-Rest](https://www.ant0ine.com/post/introducing-go-json-rest.html)\n- [(Blog Post) Better URL Routing ?](https://www.ant0ine.com/post/better-url-routing-golang.html)\n\n\n## Version 3 release notes\n\n### What's New in v3\n\n* Public Middlewares. (12 included in the package)\n* A new App interface. (the router being the provided App)\n* A new Api object that manages the Middlewares and the App.\n* Optional and interchangeable App/router.\n\n### Here is for instance the new minimal \"Hello World!\"\n\n```go\napi := rest.NewApi()\napi.Use(rest.DefaultDevStack...)\napi.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {\n        w.WriteJson(map[string]string{\"Body\": \"Hello World!\"})\n}))\nhttp.ListenAndServe(\":8080\", api.MakeHandler())\n```\n\n*All 19 examples have been updated to use the new API. [See here](https://github.com/ant0ine/go-json-rest#examples)*\n\n### Deprecating the ResourceHandler\n\nV3 is about deprecating the ResourceHandler in favor of a new API that exposes the Middlewares. As a consequence, all the Middlewares are now public, and the new Api object helps putting them together as a stack. Some default stack configurations are offered. The router is now an App that sits on top of the stack of Middlewares. Which means that the router is no longer required to use Go-Json-Rest.\n\n*Design ideas and discussion [See here](https://github.com/ant0ine/go-json-rest/issues/110)*\n\n\n## Migration guide from v2 to v3\n\nV3 introduces an API change (see [Semver](http://semver.org/)). But it was possible to maintain backward compatibility, and so, ResourceHandler still works.\nResourceHandler does the same thing as in V2, **but it is now considered as deprecated, and will be removed in a few months**. In the meantime, it logs a\ndeprecation warning.\n\n### How to map the ResourceHandler options to the new stack of middlewares ?\n\n* `EnableGzip bool`: Just include GzipMiddleware in the stack of middlewares.\n* `DisableJsonIndent bool`: Just don't include JsonIndentMiddleware in the stack of middlewares.\n* `EnableStatusService bool`: Include StatusMiddleware in the stack and keep a reference to it to access GetStatus().\n* `EnableResponseStackTrace bool`: Same exact option but moved to RecoverMiddleware.\n* `EnableLogAsJson bool`: Include AccessLogJsonMiddleware, and possibly remove AccessLogApacheMiddleware.\n* `EnableRelaxedContentType bool`: Just don't include ContentTypeCheckerMiddleware.\n* `OuterMiddlewares []Middleware`: You are now building the full stack, OuterMiddlewares are the first in the list.\n* `PreRoutingMiddlewares []Middleware`: You are now building the full stack, OuterMiddlewares are the last in the list.\n* `Logger *log.Logger`: Same option but moved to AccessLogApacheMiddleware and AccessLogJsonMiddleware.\n* `LoggerFormat AccessLogFormat`: Same exact option but moved to AccessLogApacheMiddleware.\n* `DisableLogger bool`: Just don't include any access log middleware.\n* `ErrorLogger *log.Logger`: Same exact option but moved to RecoverMiddleware.\n* `XPoweredBy string`: Same exact option but moved to PoweredByMiddleware.\n* `DisableXPoweredBy bool`: Just don't include PoweredByMiddleware.\n\n\n## Version 2 release notes\n\n* Middlewares, the notion of middleware is now formally defined. They can be setup as global pre-routing Middlewares wrapping all the endpoints, or on a per endpoint basis.\nIn fact the internal code of **go-json-rest** is itself implemented with Middlewares, they are just hidden behind configuration boolean flags to make these very common options even easier to use.\n\n* A new ResponseWriter. This is now an interface, and allows Middlewares to wrap the writer. The provided writer implements, in addition of *rest.ResponseWriter*, *http.Flusher*, *http.CloseNotifier*, *http.Hijacker*, and *http.ResponseWriter*. A lot more Go-ish, and very similar to `net/http`.\n\n* The AuthBasic and CORS Middlewares have been added. More to come in the future.\n\n* Faster, more tasks are performed at init time, and less for each request.\n\n* New documentation, with more examples.\n\n* A lot of other small improvements, See the [Migration guide to v2](#migration-guide-from-v1-to-v2)\n\n\n## Migration guide from v1 to v2\n\n**Go-Json-Rest** follows [Semver](http://semver.org/) and a few breaking changes have been introduced with the v2.\n\n\n#### The import path has changed to `github.com/ant0ine/go-json-rest/rest`\n\nThis is more conform to Go style, and makes [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) work.\n\nThis:\n``` go\nimport (\n        \"github.com/ant0ine/go-json-rest\"\n)\n```\nhas to be changed to this:\n``` go\nimport (\n        \"github.com/ant0ine/go-json-rest/rest\"\n)\n```\n\n\n#### rest.ResponseWriter is now an interface\n\nThis change allows the `ResponseWriter` to be wrapped, like the one of the `net/http` package.\nThis is much more powerful, and allows the creation of Middlewares that wrap the writer. The gzip option, for instance, uses this to encode the payload (see gzip.go).\n\nThis:\n``` go\nfunc (w *rest.ResponseWriter, req *rest.Request) {\n        ...\n}\n```\nhas to be changed to this:\n``` go\nfunc (w rest.ResponseWriter, req *rest.Request) {\n        ...\n}\n```\n\n\n#### SetRoutes now takes pointers to Route\n\nInstead of copying Route structures everywhere, pointers are now used. This is more elegant, more efficient, and will allow more sophisticated Route manipulations in the future (like reverse route resolution).\n\nThis:\n``` go\nhandler.SetRoutes(\n\t\trest.Route{\n\t\t      // ...\n\t\t},\n)\n```\nhas to be changed to this:\n``` go\nhandler.SetRoutes(\n\t\t\u0026rest.Route{\n\t\t      // ...\n\t\t},\n)\n```\n\n\n####  The notion of Middleware is now formally defined\n\nA middleware is an object satisfying this interface:\n``` go\ntype Middleware interface {\n\tMiddlewareFunc(handler HandlerFunc) HandlerFunc\n}\n```\n\nCode using PreRoutingMiddleware will have to be adapted to provide a list of Middleware objects.\nSee the [Basic Auth example](https://github.com/ant0ine/go-json-rest-examples/blob/master/auth-basic/main.go).\n\n\n#### Flush(), CloseNotify() and Write() are not directly exposed anymore\n\nThey used to be public methods of the ResponseWriter. The implementation is still there but a type assertion of the corresponding interface is now necessary.\nRegarding these features, a rest.ResponseWriter now behaves exactly as the http.ResponseWriter implementation provided by net/http.\n\nThis:\n``` go\nwriter.Flush()\n```\nhas to be changed to this:\n``` go\nwriter.(http.Flusher).Flush()\n```\n\n\n#### The /.status endpoint is not created automatically anymore\n\nThe route has to be manually defined.\nSee the [Status example](https://github.com/ant0ine/go-json-rest-examples/blob/master/status/main.go).\nThis is more flexible (the route is customizable), and allows combination with Middlewarres.\nSee for instance how to [protect this status endpoint with the AuthBasic middleware](https://github.com/ant0ine/go-json-rest-examples/blob/master/status-auth/main.go).\n\n\n#### Request utility methods have changed\n\nOverall, they provide the same features, but with two methods instead of three, better names, and without the confusing `UriForWithParams`.\n\n- `func (r *Request) UriBase() url.URL` is now `func (r *Request) BaseUrl() *url.URL`, Note the pointer as the returned value.\n\n- `func (r *Request) UriForWithParams(path string, parameters map[string][]string) url.URL` is now `func (r *Request) UrlFor(path string, queryParams map[string][]string) *url.URL`.\n\n- `func (r *Request) UriFor(path string) url.URL` has be removed.\n\n\n## Thanks\n\n- [Franck Cuny](https://github.com/franckcuny)\n- [Yann Kerhervé](https://github.com/yannk)\n- [Ask Bjørn Hansen](https://github.com/abh)\n- [Paul Lam](https://github.com/Quantisan)\n- [Thanabodee Charoenpiriyakij](https://github.com/wingyplus)\n- [Sebastien Estienne](https://github.com/sebest)\n- [Edward Bramanti](https://github.com/jadengore)\n\n\nCopyright (c) 2013-2016 Antoine Imbert\n\n[MIT License](https://github.com/ant0ine/go-json-rest/blob/master/LICENSE)\n\n[![Analytics](https://ga-beacon.appspot.com/UA-309210-4/go-json-rest/master/readme)](https://github.com/igrigorik/ga-beacon)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fant0ine%2Fgo-json-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fant0ine%2Fgo-json-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fant0ine%2Fgo-json-rest/lists"}