目前我有太多重復的處理程式代碼:
type GuestMux struct {
http.ServeMux
}
func main() {
guestMux := NewGuestMux()
http.ListenAndServe(":3001", guestMux)
}
func NewGuestMux() *GuestMux {
var guestMux = &GuestMux{}
guestMux.HandleFunc("/guest/createguest", createGuestHandler)
guestMux.HandleFunc("/guest/updateguest", updateGuestHandler)
guestMux.HandleFunc("/guest/getguest", getGuestHandler)
return guestMux
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
var createGuestReq CreateGuestRequest
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
err = json.Unmarshal(reqBody, &createGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
resp, err := CreateGuest(&createGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
err = json.Unmarshal(reqBody, &updateGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
resp, err := UpdateGuest(&updateGuestReq)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func getGuestHandler(w http.ResponseWriter, r *http.Request) {
// almost the same as above two handlers, just different method to call and
// its parameter type
...
}
有沒有更好的方法來撰寫處理程式createGuestHandler
,updateGuestHandler
而getGuestHandler
不是重復類似的代碼塊三次。我想我可以使用interface
,但不知道如何寫。我有大約 20 個處理程式,因此重復代碼似乎并不真正可維護。
//stackoverflow 不允許對細節有太多代碼的問題,所以......這里有細節,那里有細節,甚至更多細節......//
uj5u.com熱心網友回復:
您可以將通用邏輯移至單獨的函式,并將每個處理程式中特定的所有內容傳遞給它。
假設您具有以下型別和功能:
type CreateGuestRequest struct{}
type UpdateGuestRequest struct{}
type CreateGuestResponse struct{}
type UpdateGuestResponse struct{}
func CreateGuest(v *CreateGuestRequest) (resp *CreateGuestResponse, err error) {
return nil, nil
}
func UpdateGuest(v *UpdateGuestRequest) (resp *UpdateGuestResponse, err error) {
return nil, nil
}
允許使用泛型
如果允許泛型,您可以將所有代碼從處理程式中分解出來:
func handle[Req any, Resp any](w http.ResponseWriter, r *http.Request, logicFunc func(dst Req) (Resp, error)) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, CreateGuest)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
handle(w, r, UpdateGuest)
}
如您所見,所有處理程式實作都只是一行!我們現在甚至可以擺脫處理函式,因為我們可以從邏輯函式(如CreateGuest()
, UpdateGuest()
)創建處理程式。
這就是它的樣子:
func createHandler[Req any, Resp any](logicFunc func(dst Req) (Resp, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var dst Req
if err := json.NewDecoder(r.Body).Decode(&dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc(dst)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
}
并使用它:
func NewGuestMux() *GuestMux {
var guestMux = &GuestMux{}
guestMux.HandleFunc("/guest/createguest", createHandler(CreateGuest))
guestMux.HandleFunc("/guest/updateguest", createHandler(UpdateGuest))
return guestMux
}
沒有泛型
該解決方案不使用泛型(也適用于舊的 Go 版本)。
func handle(w http.ResponseWriter, r *http.Request, dst interface{}, logicFunc func() (interface{}, error)) {
if err := json.NewDecoder(r.Body).Decode(dst); err != nil {
log.Printf("Decoding body failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := logicFunc()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("Encoding response failed: %v", err)
}
}
func createGuestHandler(w http.ResponseWriter, r *http.Request) {
var createGuestReq CreateGuestRequest
handle(w, r, &createGuestReq, func() (interface{}, error) {
return CreateGuest(&createGuestReq)
})
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
handle(w, r, &updateGuestReq, func() (interface{}, error) {
return UpdateGuest(&updateGuestReq)
})
}
uj5u.com熱心網友回復:
這里有很多避免重復的方法,例如,您可以使用裝飾器模式,您可以在其中定義如何解碼/編碼以及其他不包含業務邏輯的步驟。
您可以查看兩種有趣的方法:一種來自 Mat:https ://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
另一個是 go-kit 包(您可以在 github 上查看),但我建議您查看有關如何撰寫裝飾器而不是安裝庫的想法,這可能對您的實施有點過分。
uj5u.com熱心網友回復:
通常 REST API 僅/guest
具有帶有單個處理程式的端點,該處理程式根據HTTP 方法決定要做什么:
POST
創造GET
檢索PUT
更新整個記錄PATCH
更新某些欄位
您可以查看r.Method
處理程式內部并據此決定運行哪些代碼。
如果您必須使用問題中顯示的介面,例如,您可以將處理程式包裝到具有預期介面的匿名函式,并使其接受一個附加引數來決定像這樣做什么:
guestMux.HandleFunc("/guest/createguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, CREATE)
})
guestMux.HandleFunc("/guest/updateguest", func(w http.ResponseWriter, r *http.Request) {
guestHandler(r, w, UPDATE)
})
...
(其中 CREATE 和 UPDATE 是某種標志,告訴guestHandler()
它應該做什么)
uj5u.com熱心網友回復:
我建議看看go-kit。它主要設計用于使用 Hexagonal 架構創建服務。它帶來了很多實用功能,避免重復代碼,專注于業務邏輯。
它有很多可能不需要的功能,但由于它是一個工具包(而不是一個完整的框架),您可以只使用您需要的部分。
例子也很容易理解。
uj5u.com熱心網友回復:
我有這些實用功能 : decodeJsonBody
,respondJson
我用它們來簡化回應,而不會增加太多復雜性。我將它包裝在Response
用于發送客戶端錯誤詳細資訊的結構中。
type Response struct {
Data interface{} `json:"data"`
Errors interface{} `json:"errors"`
}
func respondJson(w http.ResponseWriter, data interface{}, err error) {
w.Header().Set("Content-Type", "application/json")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
err = json.NewEncoder(w).Encode(Response{
Errors: err.Error(),
})
return
}
err = json.NewEncoder(w).Encode(Response{
Data: data,
})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("http handler failed to convert response to json %s\n", err)
}
}
func decodeJsonBody(r *http.Request, v interface{}) error {
decoder := json.NewDecoder(r.Body)
return decoder.Decode(v)
}
func updateGuestHandler(w http.ResponseWriter, r *http.Request) {
var updateGuestReq UpdateGuestRequest
err := decodeJsonBody(r, &updeateGuestReq)
if err != nil {
respondJson(w, nil, err)
return
}
data, err := UpdateGuest(&updateGuestReq)
respondJson(w, data, err)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/473044.html
上一篇:HCL解碼:具有多個標簽的塊
下一篇:處理恐慌后繼續執行函式