基於Go語言快速構建RESTful API服務


In this post, we will not only cover how to use Go to create a RESTful JSON API, but we will also talk about good RESTful design.

部分內容刪減調整,原文請查看: Making a RESTful JSON API in Go,2014Nov

Author:CORY LANOU:a full stack technologist who has specialized in start-ups for the last 17 years. I'm currently working at InfluxDB on their core data team. I also help lead and organizer several community technology meetups and do Go training.

What is a JSON API?

JSON API 是數據交互規范,用以定義客戶端如何獲取與修改資源,以及服務器如何響應對應請求。JSON API設計用來最小化請求的數量,以及客戶端與服務器間傳輸的數據量。通過遵循共同的約定,可以提高開發效率,利用更普遍的工具,基於 JSON API 的客戶端還能夠充分利用緩存,以提升性能。(更多:http://jsonapi.org.cn/format/)。

示例:

{
  "links": { "posts.author": { "href": "http://example.com/people/{posts.author}", "type": "people" }, "posts.comments": { "href": "http://example.com/comments/{posts.comments}", "type": "comments" } }, "posts": [{ "id": "1", "title": "Rails is Omakase", "links": { "author": "9", "comments": [ "5", "12", "17", "20" ] } }] } 

啟動一個RESTful服務

$ go run main.go $ curl http://localhost:8080 Hello,"/" 
package main import ( "fmt" "html" "log" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil)) } 

增加路徑分發功能

路徑又稱"終點"(endpoint),表示API的具體網址。在RESTful架構中,每個網址代表一種資源(resource)。 第三方組件(Gorilla Mux package): “github.com/gorilla/mux”

package main import ( "fmt" "log" "net/http" "github.com/gorilla/mux" ) func main() { router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/", Index) router.HandleFunc("/todos", TodoIndex) router.HandleFunc("/todos/{todoId}", TodoShow) log.Fatal(http.ListenAndServe(":8080", router)) } func Index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Welcome!") } func TodoIndex(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Todo Index!") } func TodoShow(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) todoId := vars["todoId"] fmt.Fprintln(w, "Todo show:", todoId) } 

訪問測試:

$ curl http://localhost:8080/todo 404 page not found $ curl http://localhost:8080/todos Todo Index! ,"/todos" $ curl http://localhost:8080/todos/{123} TodoShow: ,"123" 

抽象數據模型

創建一個數據模型“Todo”、“Routes”。在其它語言中,使用類(class)實現。 在Go語言中,沒有class,必須使用結構(struct)。

Todo.go

package main import "time" type Todo struct { Id int `json:"id"` Name string `json:"name"` Completed bool `json:"completed"` Due time.Time `json:"due"` } type Todos []Todo 

Routes.go

package main import ( "net/http" "github.com/gorilla/mux" ) type Route struct { Name string Method string Pattern string HandlerFunc http.HandlerFunc } type Routes []Route 

重構:Handlers & Router

Handlers.go

package main import ( "encoding/json" "fmt" "net/http" "github.com/gorilla/mux" ) func Index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Welcome!") } func TodoIndex(w http.ResponseWriter, r *http.Request) { todos := Todos{ Todo{Name: "Write presentation"}, Todo{Name: "Host meetup"}, } if err := json.NewEncoder(w).Encode(todos); err != nil { panic(err) } } func TodoShow(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) todoId := vars["todoId"] fmt.Fprintln(w, "Todo show:", todoId) } 

Router.go

package main import ( "net/http" "github.com/gorilla/mux" ) func NewRouter() *mux.Router { router := mux.NewRouter().StrictSlash(true) for _, route := range routes { var handler http.Handler handler = route.HandlerFunc handler = Logger(handler, route.Name) router. Methods(route.Method). Path(route.Pattern). Name(route.Name). Handler(handler) } return router } 

啟動入口是不是清爽很多!

Main.go

Main.go package main import ( "log" "net/http" ) func main() { router := NewRouter() log.Fatal(http.ListenAndServe(":8080", router)) } 

web access:http://localhost:8080/todos

Todo Index! ,"/todos" [ { "id":0, "name":"Write sth ....", "completed":false, "due":"0001-01-01T00:00:00 }, { "id":1, "name":"Host meetup ....", "completed":false, "due":"0001-01-01T00:00:00Z" } ]

增強功能:持久化

func TodoCreate(w http.ResponseWriter, r *http.Request) { var todo Todo //add Todo instance } 

增強功能:日志

2017/05/23 15:57:23 http: multiple response.WriteHeader calls 2017/05/23 15:57:23 GET /todos TodoIndex 6.945807ms 2017/05/23 16:18:40 http: multiple response.WriteHeader calls 2017/05/23 16:18:40 GET /todos TodoIndex 2.127435ms 

Things We Didn’t Do

  1. 版本控制 API版本迭代 & 跨版本資源訪問。常用做法是將版本號放在URL,較為簡潔,例如:https://localhost:8080/v1/ 另一種做法是將版本號放在HTTP頭信息中。

  2. 授權驗證:涉及到OAuth和JWT。 (1)OAuth 2.0,OAuth2 is an authentication framework,RFC 6749 OAuth2是一種授權框架,提供了一套詳細的、可供實踐的指導性解決方案。OAuth 2.0定義了四種授權方式。授權碼模式(authorization code)、簡化模式(implicit)、密碼模式(resource owner password credentials)、客戶端模式(client credentials)。

(2)JSON web tokens,JWT is an authentication protocol,RFC 7519 JWT是一種安全協議。基本思路就是用戶提供用戶名和密碼給認證服務器,服務器驗證用戶提交信息信息的合法性;如果驗證成功,會產生並返回一個Token(令牌),用戶可以使用這個token訪問服務器上受保護的資源。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

header:定義算法(alg:ALGORITHM)和TOKEN TYPE(typ)

{
  "alg": "HS256", "typ": "JWT" } 

Data:

{
  "sub": "1234567890", "name": "John Doe", "admin": true } 
  1. eTags:關於緩存、性能和用戶標識和追蹤。

 

https://my.oschina.net/zijingshanke/blog/907955


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM