package response import ( "bytes" "net/http" "strings" jsoniter "github.com/json-iterator/go" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type bodyWriter struct { http.ResponseWriter status int buf bytes.Buffer } type responseData struct { Code int32 `json:"code"` Data any `json:"data"` Msg string `json:"msg"` } func (w *bodyWriter) WriteHeader(statusCode int) { w.status = statusCode } func (w *bodyWriter) Write(b []byte) (int, error) { return w.buf.Write(b) } func Wrap(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { bw := &bodyWriter{ResponseWriter: w, status: http.StatusOK} next(bw, r) ctrl := BaseController{} if bw.status != http.StatusOK { ctrl.Error(w, mapHTTPError(bw.status, bw.buf.String())) return } raw := bytes.TrimSpace(bw.buf.Bytes()) var data responseData if len(raw) > 0 { if err := jsoniter.Unmarshal(raw, &data); err != nil { ctrl.Fail(w) return } } ctrl.OutPut(w, data.Code, unwrapJSONData(data.Data), data.Msg) } } // unwrapJSONData 将 RPC Response.data(proto string)里的 JSON 文本还原为对象/数组/数字。 func unwrapJSONData(v any) any { s, ok := v.(string) if !ok { return v } s = strings.TrimSpace(s) if s == "" { return nil } var parsed any if err := jsoniter.Unmarshal([]byte(s), &parsed); err != nil { return v } return parsed } func mapHTTPError(httpStatus int, body string) Error { msg := strings.TrimSpace(body) msg = strings.TrimPrefix(msg, "rpc error: ") if i := strings.Index(msg, "desc = "); i >= 0 { msg = strings.TrimSpace(msg[i+len("desc = "):]) } if msg == "" { msg = Fail.GetMsg() } switch httpStatus { case http.StatusBadRequest: return Define(ErrorMissingParams, msg) case http.StatusUnauthorized: return Define(ErrorNoLogin, msg) case http.StatusForbidden: return Define(ErrorAuthority, msg) case http.StatusNotFound: return Define(ErrorDataNotExist, msg) default: return Define(Fail, msg) } } func FromGRPC(err error) Error { if err == nil { return OK } st, ok := status.FromError(err) if !ok { return Define(Fail, err.Error()) } msg := st.Message() if msg == "" { msg = st.Code().String() } switch st.Code() { case codes.InvalidArgument: return Define(ErrorMissingParams, msg) case codes.Unauthenticated: return Define(ErrorNoLogin, msg) case codes.PermissionDenied: return Define(ErrorAuthority, msg) case codes.NotFound: return Define(ErrorDataNotExist, msg) case codes.AlreadyExists: return Define(ErrorDataIsExist, msg) default: return Define(Fail, msg) } }