json - JSON 请求和响应的一般处理

标签 json go

假设一个 Go 程序有几个像这样的处理函数:

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”函数和另一个处理实际 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 惯用的实现方式是什么?

最佳答案

您可以使用反射来消除样板文件。这是一个将 func(pointer) (pointer, error) 适配为处理 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 .

关于json - JSON 请求和响应的一般处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39821658/

相关文章:

go - 如何使用 google/jsonapi 和 echo 框架返回有效的 jsonapi 响应

go - 如何使用 mapstructure 标签作为 json 标签?

json - 如何在 react typescript 中从 json 文件中获取和显示数据

javascript - 将对象数据从 AJAX 发送到 PHP

c++ - QT中如何从json中加载base64图片数据

go - 在Golang中阅读BigQuery。并未给出所有预期结果。该怎么办?

go - 如何设置用于 HTTP 请求的 IP?

javascript - JSON 响应 Long 被舍入或损坏

json - play 框架 - 如何更新 json 索引数组项

javascript - 无法找到聊天消息的 Steam UMQID