//随着API接口增加,项目中会出现很多重复的代码,一种可扩展的方法就是将重复代码抽象出来
//对于以JSON作为序列化方式的接口,我们可以增加如下方法:
//方法一,从请求当中解析出,请求数据
func decodeBody(r *http.Request, v interface{}) error {
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(v)
}
//方法二,将相应数据序列化并写入http.ResponseWriter中
func encodeBody(w http.ResponseWriter, r *http.Request, v interface{}) error {
return json.NewEncoder(w).Encode(v)
}
//方法3,该方法使用方法2更简化了,需要API应答的处理过程,只需要将应答状态码和应答的数据传入即可
func respond(w http.ResponseWriter, r *http.Request, status int, data interface{}) {
w.WriteHeader(status)
if data != nil {
encodeBody(w, r, data)
}
}
//方法4,restAPI方法中,错误处理是经常碰到的,构造一个方便的错误处理会增加开发效率
func respondErr(w http.ResponseWriter, r *http.Request, status int, args ...interface{}) {
respond(w, r, status, map[string]interface{}{
"error": map[string]interface{}{
"message": fmt.Sprint(args...),
},
})
}
//方法5,将方法4封装成http错误处理
func respondHTTPErr(w http.ResponseWriter, r *http.Request, status int) {
respondErr(w, r, status, http.StatusText(status))
}
//API handlers之间共享数据方法:
type contextKey struct {
name string
}
var contextKeyAPIKey = &contextKey{"api-key"}
func APIKey(ctx context.Context) (string, bool) {
key, ok := ctx.Value(contextKeyAPIKey).(string)
return key, ok
}
//API处理函数封装,函数1
func withAPIKey(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
key := r.URL.Query().Get("key")
if !isValidAPIKey(key) {
respondErr(w, r, http.StatusUnauthorized, "invalid API key")
return
}
ctx := context.WithValue(r.Context(), contextKeyAPIKey, key)
fn(w, r.WithContext(ctx))
}
}
func isValidAPIKey(key string) bool {
return key == "abc123"
}
//方法2,处理跨域问题
func withCORS(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Expose-Headers", "Location")
fn(w, r)
}
}
type server struct{}
//API对资源的处理方式:
func (s *server) HandlResource(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
s.HandleResourceGet(w, r)
return
case "POST":
s.handleRresourcePost(w, r)
return
case "DELETE":
s.handleResourceDelete(w, r)
return
default:
respondHTTPErr(w, r, http.StatusNotFound)
}
}