Обработчики Golang обрабатывающие различные типы
Это Аппхандлеры из шаблона, который я нашел в интернете, исследуя gorilla/mux. Они являются частью структуры, которая удовлетворяет http.Обработчик. Если вы заметили, следующие два блока точно такие же. Фактически, они могут быть переданы "варианту" ("потоку" или "процессу") в виде строки.
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
}
Однако следующие два блока нуждаются не только в строке, но и в переменной соответствующего типа ("поток" и "процесс"), чтобы успешно отменить попадание, которое я получаю от 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, когда речь идет об объявленном типе. Эти обработчики тоже находятся в одном пакете, так как я думаю, что все они выполняют одну и ту же задачу. Я очень четко повторяю себя в коде, но мне нужен совет о том, как я могу улучшить. Я прошел мимо "немного копирования лучше, чем немного зависимости."но я боюсь, потому что "отражение никогда не бывает ясным".
Вот пример: пример объявления в main с использованием одной из этих функций.
api.Handle("/flow/{id:[0-9]+}", handlers.AppHandler{context, handlers.GetFlow}).Methods("GET")
2 ответа:
Хорошо, я предлагаю решение, которое даст вам максимальное повторное использование кода и минимальное копирование кода. Это, на мой взгляд, самое общее решение. Мы также примем во внимание ответ, данный https://stackoverflow.com/users/7426/adrian для завершения решения. Вам нужно только определить одну функцию, которая будет функцией более высокого порядка
CreateHandler, которая будет возвращать функцию следующей сигнатуры:func(*AppContext, http.ResponseWriter, http.Request) (int, error).Эта подпись является фактической подпись обработчика, который должен использоваться в качестве конечной точки mux. Решение включает в себя определение типа
Handler, который является структурой, имеющей три поля:•
handlerType: думайте об этом как о перечислении, имеющем либо значение"CREATE", либо"GET". Это позволит решить, какой из двух блоков кода, которые вы вставили в свой вопрос, мы должны использовать.•
handlerActionName: это подскажет"CREATE"или"GET", какой эластичный использовать. Значение должно быть либо"flow", либо"process".•
elastible: это будет ... Тип интерфейсаElastible, который будет иметь функциюSetESID. Мы будем использовать это, чтобы отправить наши типы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://stackoverflow.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://stackoverflow.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")
Надеюсь, это поможет!
Вы можете сделать это, передав пример нужного типа, так же, как это делает
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 }Этот код непроверен (потому что у меня нет вашего struct defs или другого зависимого кода), но я надеюсь, что он донесет идею.