go-zero icon indicating copy to clipboard operation
go-zero copied to clipboard

Gateway how to add data head for response body?

Open eyjian opened this issue 1 year ago • 5 comments
trafficstars

Add:

{"code":0,"data":{ ANY }

for success response body.

Add:

{"code":ERRCODE,"message":"error message"}

for fail response body.

httpx.SetErrorHandlerCtx supports fail response, but httpx.SetOkHandler does not work.

The following approach works with middleware, but is inelegant:

server.Use(wrapResponse)

type responseWriter struct {
	http.ResponseWriter
	statusCode int
	body       bytes.Buffer
}

func (rw *responseWriter) WriteHeader(statusCode int) {
	rw.statusCode = statusCode
	rw.ResponseWriter.WriteHeader(statusCode)
}

func (rw *responseWriter) Write(p []byte) (int, error) {
	return rw.body.Write(p)
}

func (rw *responseWriter) Body() []byte {
	return rw.body.Bytes()
}

// 对响应加上“"code":0,"data":{}”,
// 对于已经包含了“code”的不做任何处理(原因是 grpcErrorHandler 才能处理好)
func wrapResponse(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 记录原始响应 writer
		rw := &responseWriter{
			ResponseWriter: w,
			statusCode:     http.StatusOK,
		}

		// 执行下一个中间件或处理函数
		next.ServeHTTP(rw, r)

		// 检查响应状态码
		if rw.statusCode != http.StatusOK {
			return
		}

		// 获取原始响应数据
		var resp map[string]interface{}
		err := json.Unmarshal(rw.Body(), &resp)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// 检查响应是否已经包含 code
		if _, ok := resp["code"]; ok {
			// 如果响应已经包含 code,则直接写回原始响应正文
			w.Header().Set("Content-Type", "application/json")
			w.Write(rw.Body())
			return
		}

		// 包装响应数据
		wrappedResp := map[string]interface{}{
			"code": 0,
			"data": resp,
		}

		// 将包装后的响应数据写回 response  body
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(wrappedResp)
	})
}

type MyResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

@kevwan @gongluck @kesonan Thanks.

eyjian avatar Jan 18 '24 02:01 eyjian

Reference test cases, maybe you can use

httpx.OkJson(w, wrappedResp)

replace

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(wrappedResp)

gongluck avatar Jan 18 '24 12:01 gongluck

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


Reference test cases, maybe you can use

httpx.OkJson(w, wrappedResp)

replace

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(wrappedResp)

Issues-translate-bot avatar Jan 18 '24 12:01 Issues-translate-bot

httpx.OkJson(w, wrappedResp)

Thanks. I hope to achieve the goal without using "server.Use(wrapResponse)", which is a non-elegant way to implement it.

eyjian avatar Jan 19 '24 01:01 eyjian

@eyjian problem solved?

kevwan avatar Feb 03 '24 12:02 kevwan

@eyjian problem solved?

Thank you, it's a temporary solution, but it's not elegant and not a satisfactory way. In addition, I hope that the gateway will not be unable to start because a certain service is not started. This is unreasonable. Just because a service cannot start, the gateway cannot start. This is a big problem.

The solution is as follows: https://github.com/eyjian/mooon-gateway/blob/main/main.go

eyjian avatar Feb 03 '24 13:02 eyjian