REST API設計模式和反模式
RESTful API已經(jīng)成為構建現(xiàn)代網(wǎng)絡應用的事實標準。它們允許一個靈活和可擴展的架構,可以很容易地被廣泛的客戶端所消費。然而,設計一個既健壯又可維護的REST API是很有挑戰(zhàn)性的,特別是對于剛?cè)胄械拈_發(fā)者。
在這篇文章中,我們將探討一些常見的REST API設計模式和開發(fā)者應該注意的反模式。我們還將提供Golang和Open API Schema的代碼片段來幫助說明這些概念。

一、REST API設計模式
1.以資源為導向的架構(ROA)
面向資源的架構(ROA)是一種設計模式,強調(diào)資源在RESTful API中的重要性。資源是RESTful API的關鍵構件,它們應該被設計成易于消費和操作的方式。
在Golang中實現(xiàn)ROA的一種方式是使用gorilla/mux包進行路由。這里有一個例子:
r := mux.NewRouter()
r.HandleFunc("/users/{id}", getUser).Methods("GET")
r.HandleFunc("/users", createUser).Methods("POST")
r.HandleFunc("/users/{id}", updateUser).Methods("PUT")
r.HandleFunc("/users/{id}", deleteUser).Methods("DELETE")在Open API Schema中,你可以使用path參數(shù)來定義資源。下面是一個例子:
paths:
 /users/{id}:
 get:
 …
 put:
 …
 delete:
 …
 /users:
 post:
 …2. HATEOAS
超媒體作為應用狀態(tài)的引擎(HATEOAS)是一種設計模式,允許客戶動態(tài)地瀏覽RESTful API。API提供超媒體鏈接,客戶可以按照這些鏈接來發(fā)現(xiàn)資源并與之互動。
為了在GoLang中實現(xiàn)HATEOAS,你可以使用go-jsonapi包。這里有一個例子:
type User struct {
 ID string `json:"id"`
 Name string `json:"name"`
 Links *Links `json:"links,omitempty"`
}
type Links struct {
 Self *Link `json:"self,omitempty"`
}
type Link struct {
 Href string `json:"href,omitempty"`
}
func getUser(w http.ResponseWriter, r *http.Request) {
 userID := mux.Vars(r)["id"]
 user := User{ID: userID, Name: "John Doe"}
 user.Links = &Links{
 Self: &Link{Href: fmt.Sprintf("/users/%s", userID)},
 }
 jsonapi.MarshalOnePayload(w, &user)
}在Open API Schema中,你可以使用links參數(shù)來定義超媒體鏈接。這里有一個例子:
paths:
 /users/{id}:
 get:
 responses:
 '200':
 content:
 application/json:
 schema:
 $ref: '#/components/schemas/User'
 links:
 self:
 href: '/users/{id}'二、REST API反模式
1.RPC式的API
遠程過程調(diào)用(RPC)風格的API是RESTful API設計中一個常見的反模式。RPC風格的API暴露了直接映射到底層實現(xiàn)的方法,而不是專注于資源。
下面是一個GoLang中RPC風格API的例子:
func getUser(w http.ResponseWriter, r *http.Request) {
 userID := r.FormValue("id")
 user := userService.GetUser(userID)
 json.NewEncoder(w).Encode(user)
}在Open API Schema中,你可以使用operationId參數(shù)來定義RPC風格的API。下面是一個例子:
paths:
 /users:
 get:
 operationId: getUser2.過度的工程設計
過度工程是RESTful API設計中另一個常見的反模式。當開發(fā)者試圖預測每一個可能的用例并建立一個復雜的API來適應它們時,就會出現(xiàn)過度設計。
這里有一個Golang中過度工程的例子:
func getUser(w http.ResponseWriter, r *http.Request) {
 userID := mux.Vars(r)["id"]
 user, err := userService.GetUser(userID)
 if err != nil {
 handleError(w, err)
 return
 }
 json.NewEncoder(w).Encode(user)
}
func createUser(w http.ResponseWriter, r *http.Request) {
 var user User
 err := json.NewDecoder(r.Body).Decode(&user)
 if err != nil {
 handleError(w, err)
 return
 }
 user.ID = uuid.New().String()
 user.CreatedAt = time.Now()
 user.UpdatedAt = time.Now()
 err = userService.CreateUser(user)
 if err != nil {
 handleError(w, err)
 return
 }
 json.NewEncoder(w).Encode(user)
}
func updateUser(w http.ResponseWriter, r *http.Request) {
 userID := mux.Vars(r)["id"]
 var user User
 err := json.NewDecoder(r.Body).Decode(&user)
 if err != nil {
 handleError(w, err)
 return
 }
 user.ID = userID
 user.UpdatedAt = time.Now()
 err = userService.UpdateUser(user)
 if err != nil {
 handleError(w, err)
 return
 }
 json.NewEncoder(w).Encode(user)
}
func deleteUser(w http.ResponseWriter, r *http.Request) {
 userID := mux.Vars(r)["id"]
 err := userService.DeleteUser(userID)
 if err != nil {
 handleError(w, err)
 return
 }
 w.WriteHeader(http.StatusNoContent)
}
func handleError(w http.ResponseWriter, err error) {
 w.WriteHeader(http.StatusInternalServerError)
 fmt.Fprint(w, err. Error())
}在Open API Schema中,你可以使用x-go-genie擴展定義過度工程。這里有一個例子:
paths:
 /users/{id}:
   get:
     x-go-genie:
       serviceName: UserService
       methodName: GetUser
   put:
     x-go-genie:
       serviceName: UserService
       methodName: UpdateUser
   delete:
     x-go-genie:
       serviceName: UserService
       methodName: DeleteUser
 /users:
   post:
     x-go-genie:
       serviceName: UserService
       methodName: CreateUser總結(jié)
設計一個既健壯又可維護的RESTful API可能具有挑戰(zhàn)性,但通過遵循最佳實踐并避免常見的反模式,開發(fā)人員可以創(chuàng)建易于消費和操作的API。在這篇文章中,我們探討了一些常見的REST API設計模式和反模式,并提供了GoLang和Open API Schema的代碼片段來幫助說明這些概念。















 
 
 










 
 
 
 