在GO中制作休息处理程序的有效方法(无重复代码)?
目前,我为处理程序有太多重复的代码:
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
,updateguesuesthandler
和getGuestHandler
而不是重复类似的类似方法代码块三次。我想我可以使用接口
,但不确定如何编写。我有大约20个处理程序,因此重复代码似乎无法真正维护。
// Stackoverflow不允许详细信息过多的代码,因此...在此处的详细信息,详细信息,甚至更多详细信息... //
Currently I have too much repeated code for the handlers:
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
...
}
Is there any nicer way to write the handlers createGuestHandler
, updateGuestHandler
and getGuestHandler
instead of repeating similar code blocks three times. I guess I can use interface
but am not sure how to write that. I have about 20 handlers so the repeating code does not seem really maintainable.
//stackoverflow does not allow question with too much code over details so... details here, details there, even more details...//
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可以将通用逻辑移至单独的函数,并将所有处理程序中特定的所有内容传递给它。
让我们假设您具有这些类型和功能:
如果允许使用仿制药
,则可以将全部代码从处理程序中出发:
如您所见,所有处理程序实现只是一行!我们现在甚至可以摆脱处理程序功能,因为我们可以从逻辑函数中创建一个处理程序(例如
createguest()
,updateguesuest()
)。这就是它的外观:
使用它:
没有仿制药
,该解决方案不使用仿制药(并且也可以与旧版本一起使用)。
You can move the common logic to a separate function, and pass everything to it that is specific in each handler.
Let's assume you have these types and functions:
With generics allowed
If generics are allowed, you can factor all code out of handlers:
As you can see, all handler implementations are just a single line! We can even get rid of the handler functions now, as we can create a handler from a logic function (like
CreateGuest()
,UpdateGuest()
).This is how it would look like:
And using it:
Without generics
This solution does not use generics (and works with old Go versions too).
例如,这里有很多方法可以避免重复重复,例如,您可以使用装饰器模式,在其中您可以定义如何解码/编码和其他不包含业务逻辑的步骤。
您可以检查两种有趣的方法:
一个来自mat: https://pace.dev/blog/2018/05/09/how-i-i-write-http-services-services-after-eight-eights.html
另一个是Go-Kit软件包(您可以在GitHub上查看它,但我建议您检查有关如何组合装饰器而不是安装图书馆的想法,这可能是您启动的过度杀伤。
There are many ways to avoid repetition here, for example, you could use a decorator pattern, where you can define how to decode/encode and other steps that doesn't include your business logic.
You can check two interesting approaches:
One is from Mat: https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
The other one is the go-kit package (you can check it out on github), but I recommend you to checkout the idea on how to compose decorators instead of installing the library, could be an overkill for your implematation.
通常,REST API只有
/guest
带有单个处理程序的端点,该端点决定基于 http方法:post
createget
get 检索put
以更新整个记录您可以在处理程序内部查看
r.method
,并根据此确定要运行的代码。如果您的问题中显示的界面注定,则可以EG包装处理程序到具有预期界面的匿名函数,并使其接受一个额外的参数来决定该怎么做:(
在其中创建和更新是某种标志,这些标志告诉<代码> GuestHandler()它应该做什么)
Typically REST APIs have just
/guest
endpoint with single handler that decides what to do based on HTTP method:POST
to createGET
to retrievePUT
to update the entire recordPATCH
to update certain fieldsYou can look at
r.Method
inside your handler and decide what code to run based on that.If you are bound to interface shown in your question you can e.g. wrap handler to an anonymous function with expected interface and make it accept an additional argument to decide what to do like this:
(where CREATE and UPDATE are some sort of flags that tell
guestHandler()
what it should do)我建议您看看 go-kit 。
它主要设计用于使用六角形体系结构创建服务。它带来了许多实用功能,以避免重复代码并专注于业务逻辑。
它具有很多可能不需要的功能,但是由于它是工具包(而不是完整的框架),因此您只需使用所需的零件。
示例也很容易遵循。
I suggest to have a look to go-kit.
It's mainly designed to create services using Hexagonal architecture. It brings a lot of utility functions to avoid repeated code and focus on the business logic.
It has a lot of functionality that may not need but since it's a toolkit (and not a complete framework) you're free to use only the parts that you need.
Examples are also easy to follow.
我具有这些实用程序函数:
decodejsonbody
,recondjson
我用来简化响应,而不会添加过多的复杂性。我将其包装在响应
struct中,用于发送客户端错误详细信息。I have these utility functions :
decodeJsonBody
,respondJson
that I use to simplify response, without adding too much complexity. I wrap it in theResponse
struct for sending client side error details.