JSON请求和响应

问题描述:

假设与几个处理函数这样的围棋程序的一般处理:JSON请求和响应

type FooRequest struct { 
    FooField string `json:"foofield"` 
    // ... 
} 

type FooResponse struct { 
    BarField string `json:"barfield"` 
    // ... 
} 

func handleFoo(w http.ResponseWriter, r *http.Request) { 
    var req FooRequest 
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 

    // do what actually matters: 

    foo := DoStuff(req) 

    baz, err := DoSomething(foo) 
    if err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError)    
     return 
    } 

    resp := DoEvenMoreStuff(baz) 

    // back to boiler plate: 

    if err := json.NewEncoder(w).Encode(resp); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError)    
     return 
    } 

} 

怎么会这样的代码进行重构,以避免JSON解码/编码锅炉板?

我可能会看到一个通用的“手柄JSON” FUNC和另一FUNC处理实际foo的东西:

func handleJson(w http.ResponseWriter, r *http.Request) { 
    var req FooRequest // what about this line? 
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 

    resp, err := handleFooElegantly(req) 
    if err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 

    if err := json.NewEncoder(w).Encode(resp); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 
} 

func handleFoo(req FooRequest) (FooResponse, error) { 
    var resp FooResponse 
    foo := DoStuff(req) 

    baz, err := DoSomething(foo) 
    if err != nil { 
     return resp, err 
    } 

    resp = DoEvenMoreStuff(baz) 

    return resp, nil 
} 

这给我们留下了告诉JSON解码器,应尽量类型的问题解码。

什么是实施这种惯用Go方式?

您可以使用反射消除样板。下面是一个将函数(指针)(指针,错误)调整为处理JSON编码和解码的http.Handler的示例。

type jsonHandler struct { 
    fn interface{} 
} 

func (jh jsonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    // allocate the input argument 
    v := reflect.ValueOf(jh.fn) 
    arg := reflect.New(v.Type().In(0).Elem()) 

    // decode to the argument 
    if err := json.NewDecoder(r.Body).Decode(arg.Interface()); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 

    // invoke the function 
    out := v.Call([]reflect.Value{arg}) 

    // check error 
    if err, ok := out[1].Interface().(error); ok && err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 

    // decode result 
    if err := json.NewEncoder(w).Encode(out[0].Interface()); err != nil { 
     http.Error(w, err.Error(), http.StatusInternalServerError) 
     return 
    } 
} 

这里有一个如何使用该适配器的例子:如果函数没有一个单一的指针参数,并返回一个指针和一个错误

type Input struct { 
    A, B int 
} 

type Output struct { 
    Result int 
} 

func add(in *Input) (*Output, error) { 
    return &Output{Result: in.A + in.B}, nil 
} 

handler := jsonHandler{add} // handler satisfies http.Handler 

working playground example

代码将恐慌。在返回处理程序之前,更健壮和完整的实现应该检查函数是否满足这些约束。

func newHandler(fn interface{)) (http.Handler, error) { 
    // use reflect to check fn, return error 
    ... 

    return jsonHandler{fn}, nil 
} 

在这个答案中使用反射有点类似to an approach used in the standard library