处理不同类型的 Golang 处理程序
Golang handlers handling different types
这些是我在研究 gorilla/mux 时在网上找到的模式中的 AppHandler。它们是满足 http.Handler 的结构的一部分。如果您注意到,以下两个块完全相同。实际上,它们可以作为字符串传递给 'variant'("flow" 或 "process")。
func CreateFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("flow", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func CreateProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("process", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
但是,以下两个块不仅需要字符串,还需要关联类型的变量("Flow" 和 "Process")才能成功解组我从 ElasticSearch 获得的命中。除此之外,它们是相同的代码。
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("flow", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var flow Flow
err = json.Unmarshal(*hit.Source, &flow)
if err != nil {
return 500, err
}
flow.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(flow)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("process", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var process Process
err = json.Unmarshal(*hit.Source, &process)
if err != nil {
return 500, err
}
process.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(process)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
当涉及声明的类型时,我不确定如何在 golang 中概括此行为。这些处理程序也都在同一个包中,因为我认为它们都在完成类似的任务。我在代码中非常清楚地重复自己,但我需要有关如何改进的建议。我已经过了 "a little copying is better than a little dependency." 但我很害怕,因为 "reflection is never clear"。
这是使用其中一个函数在 main 中声明的示例。
api.Handle("/flow/{id:[0-9]+}", handlers.AppHandler{context, handlers.GetFlow}).Methods("GET")
您可以通过传入必要类型的范例来完成此操作,方法与 Unmarshal
相同:
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"flow",new(Flow))
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"process",new(Process))
}
func GetThing(a *AppContext, w http.ResponseWriter, r *http.Request, t string, ob Elastible{}) (int, error) {
hit, code, err := a.GetByID(t, mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
ob.SetESID(hit.Id)
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(ob)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
type Elastible interface {
SetESID(id ESIDType) // whatever type ESID is, not clear from example
}
func (f *Flow) SetESID(id ESIDType) {
f.ESID = id
}
此代码未经测试(因为我没有您的结构定义或其他相关代码)但我希望它能理解这个想法。
好吧,我提出了一个解决方案,它将为您提供最大的代码重用和最少的代码复制。在我看来,这是迄今为止最通用的解决方案。我们还将考虑 https://whosebug.com/users/7426/adrian 给出的答案来完成解决方案。您只需定义一个函数,它将是一个高阶函数 CreateHandler
,它将 return 具有以下签名的函数:
func(*AppContext, http.ResponseWriter, http.Request) (int, error)
.
此签名是要用作多路复用端点的处理程序的实际签名。该解决方案涉及定义一个 Handler
类型,它是一个具有三个字段的结构:
• handlerType
:将其视为具有值"CREATE"
或"GET"
的枚举。这将决定我们应该使用您在问题中粘贴的两个代码块中的哪一个。
• handlerActionName
:这将告诉"CREATE"
或"GET"
使用哪个Elastible。值应为 "flow"
或 "process"
.
• elastible
:这将具有SetESID
功能的接口类型Elastible
。我们将使用它来将我们的 Flow
或 Process
类型发送到我们的 Handler
。因此 Flow
和 Process
都应该满足我们的接口。
这将使解决方案更加通用,并且只会调用 handler.elastible.SetESID()
并且我们将插入 ESID 而不管 'elastible' 中的基础类型可以是 'Flow' 或 'Process'
我还定义了一个 sendResponse(response interface{})
函数,我们将重新使用它来发送响应。它使用闭包获取 w http.ResponseWriter
。 response
因此可以是任何东西,一个
struct {
Highest int `json:"id"`
}{highest}
或一个 Flow
或一个 Process
。这也将使该函数通用。
现在完整的解决方案是。
// This is the type that will be used to build our handlers.
type Handler struct {
handlerType string // Can be "CREATE" or "GET"
handlerActionName string // Can be "flow" or "process"
elastible Elastible // Can be *Flow or *Process
}
// Your ESID Type.
type ESIDType string
// Solution proposed by https://whosebug.com/users/7426/adrian.
type Elastible interface {
SetESID(id ESIDType)
}
// Make the Flow and Process pointers implement the Elastible interface.
func (flow *Flow) SetESID(id ESIDType) {
flow.ESID = id
}
func (process *Process) SetESID(id ESIDType) {
process.ESID = id
}
// Create a Higher Order Function which will return the actual handler.
func CreateHandler(handler Handler) func(*AppContext, http.ResponseWriter, http.Request) (int, error) {
return func(a *AppContext, w http.ResponseWriter, r http.Request) (int, error) {
// Define a sendResponse function so that we may not need to copy paste it later.
// It captures w using closure and takes an interface argument that we use to call .Encode() with.
sendResponse := func(response interface{}) (int, error) {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(response)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
// Define these variables beforehand since we'll be using them
// in both the if and else block. Not necessary really.
var code int
var err error
// Check the handlerType. Is it create or get?
if handler.handlerType == "CREATE" {
var highest int
// Creates the thing using handler.handlerActionName which may be "flow" or "process"
highest, code, err = a.Create(handler.handlerActionName, r)
if code != 200 || err != nil {
return code, err
}
// Send the response using the above defined function and return.
return sendResponse(struct {
Highest int `json:"id"`
}{highest})
} else {
// This is GET handlerType.
var hit HitType
// Get the hit using again the handler.handlerActionName which may be "flow" or "process"
hit, code, err = a.GetByID(handler.handlerActionName, mux.Vars(r)["id"], r)
if code != 200 || err != nil {
return code, err
}
// Do the un-marshalling.
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
// We have set the handler.elastible to be an interface type
// which will have the SetESID function that will set the ESID in the
// underlying type that will be passed on runtime.
// So the ESID will be set for both the Flow and the Process types.
// This interface idea was given inside an earlier answer by
// https://whosebug.com/users/7426/adrian
handler.elastible.SetESID(hit.id)
return sendResponse(handler.elastible)
}
}
}
您将使用以下代码设置您的 mux 端点。
// This was your first function. "CreateFlow"
api.Handle("/createFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your second function. "CreateProcess"
api.Handle("/createProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your third function. "GetFlow"
api.Handle("/getFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "GET",
}),
}).Methods("GET")
// This was your fourth function. "GetProcess"
api.Handle("/getProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "GET",
}),
}).Methods("GET")
希望对您有所帮助!
这些是我在研究 gorilla/mux 时在网上找到的模式中的 AppHandler。它们是满足 http.Handler 的结构的一部分。如果您注意到,以下两个块完全相同。实际上,它们可以作为字符串传递给 'variant'("flow" 或 "process")。
func CreateFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("flow", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func CreateProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("process", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
但是,以下两个块不仅需要字符串,还需要关联类型的变量("Flow" 和 "Process")才能成功解组我从 ElasticSearch 获得的命中。除此之外,它们是相同的代码。
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("flow", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var flow Flow
err = json.Unmarshal(*hit.Source, &flow)
if err != nil {
return 500, err
}
flow.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(flow)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("process", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var process Process
err = json.Unmarshal(*hit.Source, &process)
if err != nil {
return 500, err
}
process.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(process)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
当涉及声明的类型时,我不确定如何在 golang 中概括此行为。这些处理程序也都在同一个包中,因为我认为它们都在完成类似的任务。我在代码中非常清楚地重复自己,但我需要有关如何改进的建议。我已经过了 "a little copying is better than a little dependency." 但我很害怕,因为 "reflection is never clear"。
这是使用其中一个函数在 main 中声明的示例。
api.Handle("/flow/{id:[0-9]+}", handlers.AppHandler{context, handlers.GetFlow}).Methods("GET")
您可以通过传入必要类型的范例来完成此操作,方法与 Unmarshal
相同:
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"flow",new(Flow))
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"process",new(Process))
}
func GetThing(a *AppContext, w http.ResponseWriter, r *http.Request, t string, ob Elastible{}) (int, error) {
hit, code, err := a.GetByID(t, mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
ob.SetESID(hit.Id)
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(ob)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
type Elastible interface {
SetESID(id ESIDType) // whatever type ESID is, not clear from example
}
func (f *Flow) SetESID(id ESIDType) {
f.ESID = id
}
此代码未经测试(因为我没有您的结构定义或其他相关代码)但我希望它能理解这个想法。
好吧,我提出了一个解决方案,它将为您提供最大的代码重用和最少的代码复制。在我看来,这是迄今为止最通用的解决方案。我们还将考虑 https://whosebug.com/users/7426/adrian 给出的答案来完成解决方案。您只需定义一个函数,它将是一个高阶函数 CreateHandler
,它将 return 具有以下签名的函数:
func(*AppContext, http.ResponseWriter, http.Request) (int, error)
.
此签名是要用作多路复用端点的处理程序的实际签名。该解决方案涉及定义一个 Handler
类型,它是一个具有三个字段的结构:
• handlerType
:将其视为具有值"CREATE"
或"GET"
的枚举。这将决定我们应该使用您在问题中粘贴的两个代码块中的哪一个。
• handlerActionName
:这将告诉"CREATE"
或"GET"
使用哪个Elastible。值应为 "flow"
或 "process"
.
• elastible
:这将具有SetESID
功能的接口类型Elastible
。我们将使用它来将我们的 Flow
或 Process
类型发送到我们的 Handler
。因此 Flow
和 Process
都应该满足我们的接口。
这将使解决方案更加通用,并且只会调用 handler.elastible.SetESID()
并且我们将插入 ESID 而不管 'elastible' 中的基础类型可以是 'Flow' 或 'Process'
我还定义了一个 sendResponse(response interface{})
函数,我们将重新使用它来发送响应。它使用闭包获取 w http.ResponseWriter
。 response
因此可以是任何东西,一个
struct {
Highest int `json:"id"`
}{highest}
或一个 Flow
或一个 Process
。这也将使该函数通用。
现在完整的解决方案是。
// This is the type that will be used to build our handlers.
type Handler struct {
handlerType string // Can be "CREATE" or "GET"
handlerActionName string // Can be "flow" or "process"
elastible Elastible // Can be *Flow or *Process
}
// Your ESID Type.
type ESIDType string
// Solution proposed by https://whosebug.com/users/7426/adrian.
type Elastible interface {
SetESID(id ESIDType)
}
// Make the Flow and Process pointers implement the Elastible interface.
func (flow *Flow) SetESID(id ESIDType) {
flow.ESID = id
}
func (process *Process) SetESID(id ESIDType) {
process.ESID = id
}
// Create a Higher Order Function which will return the actual handler.
func CreateHandler(handler Handler) func(*AppContext, http.ResponseWriter, http.Request) (int, error) {
return func(a *AppContext, w http.ResponseWriter, r http.Request) (int, error) {
// Define a sendResponse function so that we may not need to copy paste it later.
// It captures w using closure and takes an interface argument that we use to call .Encode() with.
sendResponse := func(response interface{}) (int, error) {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(response)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
// Define these variables beforehand since we'll be using them
// in both the if and else block. Not necessary really.
var code int
var err error
// Check the handlerType. Is it create or get?
if handler.handlerType == "CREATE" {
var highest int
// Creates the thing using handler.handlerActionName which may be "flow" or "process"
highest, code, err = a.Create(handler.handlerActionName, r)
if code != 200 || err != nil {
return code, err
}
// Send the response using the above defined function and return.
return sendResponse(struct {
Highest int `json:"id"`
}{highest})
} else {
// This is GET handlerType.
var hit HitType
// Get the hit using again the handler.handlerActionName which may be "flow" or "process"
hit, code, err = a.GetByID(handler.handlerActionName, mux.Vars(r)["id"], r)
if code != 200 || err != nil {
return code, err
}
// Do the un-marshalling.
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
// We have set the handler.elastible to be an interface type
// which will have the SetESID function that will set the ESID in the
// underlying type that will be passed on runtime.
// So the ESID will be set for both the Flow and the Process types.
// This interface idea was given inside an earlier answer by
// https://whosebug.com/users/7426/adrian
handler.elastible.SetESID(hit.id)
return sendResponse(handler.elastible)
}
}
}
您将使用以下代码设置您的 mux 端点。
// This was your first function. "CreateFlow"
api.Handle("/createFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your second function. "CreateProcess"
api.Handle("/createProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your third function. "GetFlow"
api.Handle("/getFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "GET",
}),
}).Methods("GET")
// This was your fourth function. "GetProcess"
api.Handle("/getProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "GET",
}),
}).Methods("GET")
希望对您有所帮助!