乐趣区

关于后端:REST-API设计模式和反模式

RESTful API 曾经成为构建古代网络应用的事实标准。它们容许一个灵便和可扩大的架构,能够很容易地被宽泛的客户端所生产。然而,设计一个既强壮又可保护的 REST API 是很有挑战性的,特地是对于刚入行的开发者。

在这篇文章中,咱们将探讨一些常见的 REST API 设计模式和开发者应该留神的反模式。咱们还将提供 Golang 和 Open API Schema 的代码片段来帮忙阐明这些概念。

REST API 设计模式

1. 以资源为导向的架构(ROA)

面向资源的架构(ROA)是一种设计模式,强调资源在 RESTful API 中的重要性。资源是 RESTful API 的要害构件,它们应该被设计成易于生产和操作的形式。

在 Golang 中实现 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 参数来定义资源。上面是一个例子:

paths:
 /users/{id}:
 get:
 …
 put:
 …
 delete:
 …
 /users:
 post:
 …

2. HATEOAS

超媒体作为利用状态的引擎(HATEOAS)是一种设计模式,容许客户动静地浏览 RESTful API。API 提供超媒体链接,客户能够依照这些链接来发现资源并与之互动。

为了在 GoLang 中实现 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 参数来定义超媒体链接。这里有一个例子:

paths:
 /users/{id}:
 get:
 responses:
 '200':
 content:
 application/json:
 schema:
 $ref: '#/components/schemas/User'
 links:
 self:
 href: '/users/{id}'

REST API 反模式

1.RPC 式的 API

近程过程调用(RPC)格调的 API 是 RESTful API 设计中一个常见的反模式。RPC 格调的 API 裸露了间接映射到底层实现的办法,而不是专一于资源。

上面是一个 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 参数来定义 RPC 格调的 API。上面是一个例子:

paths:
 /users:
 get:
 operationId: getUser

2. 适度的工程设计

适度工程是 RESTful API 设计中另一个常见的反模式。当开发者试图预测每一个可能的用例并建设一个简单的 API 来适应它们时,就会呈现适度设计。

这里有一个 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

总结

设计一个既强壮又可保护的 RESTful API 可能具备挑战性,但通过遵循最佳实际并防止常见的反模式,开发人员能够创立易于生产和操作的 API。在这篇文章中,咱们探讨了一些常见的 REST API 设计模式和反模式,并提供了 GoLang 和 Open API Schema 的代码片段来帮忙阐明这些概念。

退出移动版