forked from mrfuxi/golang-slides
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chapter6.slide
187 lines (141 loc) · 5.07 KB
/
chapter6.slide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
Golang RESTful API
何李石 七牛
* Golang RESTful API
- 什么是 RESTful API
- RESTful API 最佳实践
- 如何将 API REST 化
- 用 Go 实现一个简易的 RESTful 框架
* 什么是 RESTful API
- REST == Representational State Transfer, 表现层状态转化
- 表现层,我们看到的是资源(Resources)
- HTTP 是一种无状态的协议,资源的状态存储在服务端
- 状态转化,是指通过客户端的请求,来修改服务端资源的存储状态(CURD)
* RESTful API 最佳实践
- 资源
操作的对象,主体:名词,复数表示(举例:photos)
- 动作(GET / PUT / POST / PATCH / DELETE / ...)
修改状态的方式,符合 CURD
- 访问
永远使用 SSL
- 版本
- 版本放在 Header 中
- 或者,URL 中放主版本,Header 中放子版本
- 在 URL 中提供参数的过滤、排序、搜索
* RESTful API 最佳实践
- 提供返回值查询
- GET /ticketsfields=id,subject,name
- PUT / POST / PATCH
- 返回创建 / 更新的资源
- 返回正确的状态码
- 返回 JSON 格式数据
- 统一命名规范(JavaScript)
- 利用 HTTP 缓存
- 提供 API 限速访问
- 文档化,参考自动化文档工具:http://swagger.io/
* RESTful API 操作
.image http://ikbear.qiniudn.com/restful-api-op.png?imageView/2/w/800
* 星球大战 API(https://swapi.co/)
.image http://ikbear.qiniudn.com/star-war-api.png?imageView/2/w/1000
* Hypermedia API
.image http://ikbear.qiniudn.com/Hypermedia-API.png?imageView/2/w/1000
最佳实践参考: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
* 如何将 API REST 化
- http.ServeMux 做路由
- http.Request.Method 获取请求的“方法”,也即动作(GET / PUT / POST / ...)
* 代码
func GetBooks(rw http.ResponseWriter, request *http.Request) {
if request.Method == "GET" { // 只接受 GET 请求
rw.Write([]byte("Hello world."))
}
}
func CreateBook(rw http.ResponseWriter, request *http.Request) {
if request.Method == "POST" { // 只接受 POST 请求
rw.Write([]byte("Hello world."))
}
}
func UpdateBook(rw http.ResponseWriter, request *http.Request) {
if request.Method == "PUT" { // 只接受 PUT 请求
rw.Write([]byte("Hello world."))
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", GetBooks)
mux.HandleFunc("/new", CreateBook)
mux.HandleFunc("/update", UpdateBook)
http.ListenAndServe(":3000", mux)
}
- 重复的工作让框架去做
* 用 Go 实现一个简易的 RESTful 框架
- 资源的表示(Resources)
type Resource interface {
Get(values url.Values) (int, interface{})
Post(values url.Values) (int, interface{})
Put(values url.Values) (int, interface{})
Delete(values url.Values) (int, interface{})
}
- 动作:
GET / PUT / POST / ...
- 参数:
type url.Values map[string][]string
- 资源 + 动作 + 参数 = 一个请求
* 对外接口 API - 路由
type API struct {
mux *http.ServeMux
}
* 给资源分配 handler
type GetSupported interface {
Get(values url.Values) (int, interface{})
}
func (api *API) requestHandler(resource interface{}) http.HandlerFunc {
return func(rw http.ResponseWriter, request *http.Request) {
if request.ParseForm() != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
var handler func(url.Values, http.Header) (int, interface{}, http.Header)
switch request.Method {
case GET:
if resource, ok := resource.(GetSupported); ok {
handler = resource.Get
}
case POST:
if resource, ok := resource.(PostSupported); ok {
handler = resource.Post
}
......
default:
handler = resource.NotAllowed
}
}
* 给 handler 配置路由
func (api *API) Register(resource interface{}, paths ...string) { // 可以对应多个路径
for _, path := range paths {
// Resource 实现了什么方法就注册什么方法
api.Mux().HandleFunc(path, api.requestHandler(resource))
}
}
* 用法
type Book struct {
}
func (book Book) Get(values url.Values, headers http.Header) (int, interface{}, http.Header) {
return 200, data, nil
}
func (book Book) Post(values url.Values, headers http.Header) (int, interface{}, http.Header) {
return 200, data, nil
}
func (book Book) Put(values url.Values, headers http.Header) (int, interface{}, http.Header) {
return 200, data, nil
}
func (book Book) NotAllowed() {
return 451, data, nil
}
book := new(Book)
var api = NewAPI()
api.Register(book, "/books", "/bar", "/baz") // 注册多个路径
go api.Start(3000)
resp, err := http.Get("http://localhost:3000/books")
if err != nil {
t.Error(err)
}