Created
October 18, 2023 19:55
-
-
Save kvii/492b2fa7726500269476fcfbd6c668dc to your computer and use it in GitHub Desktop.
kratos http.Server with "code-msg-data" encoding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package server | |
import ( | |
v1 "code_msg_data/api/helloworld/v1" | |
"code_msg_data/internal/conf" | |
"code_msg_data/internal/service" | |
sj "encoding/json" | |
nt "net/http" | |
"strings" | |
"github.com/go-kratos/kratos/v2/encoding" | |
"github.com/go-kratos/kratos/v2/encoding/json" | |
"github.com/go-kratos/kratos/v2/errors" | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/go-kratos/kratos/v2/middleware/recovery" | |
"github.com/go-kratos/kratos/v2/transport/http" | |
) | |
// 最终效果 | |
// $ curl http://localhost:8000/helloworld/kvii | |
// {"code":0,"message":"success","data":{"message":"Hello kvii"}} | |
// $ curl http://localhost:8000/helloworld/err | |
// {"code":404,"message":"user not found"} | |
// NewHTTPServer new an HTTP server. | |
func NewHTTPServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *http.Server { | |
var opts = []http.ServerOption{ | |
http.Middleware( | |
recovery.Recovery(), | |
), | |
http.ErrorEncoder(DefaultErrorEncoder), // <- 关键代码 | |
http.ResponseEncoder(DefaultResponseEncoder), // <- 关键代码 | |
} | |
if c.Http.Network != "" { | |
opts = append(opts, http.Network(c.Http.Network)) | |
} | |
if c.Http.Addr != "" { | |
opts = append(opts, http.Address(c.Http.Addr)) | |
} | |
if c.Http.Timeout != nil { | |
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) | |
} | |
srv := http.NewServer(opts...) | |
v1.RegisterGreeterHTTPServer(srv, greeter) | |
return srv | |
} | |
// DefaultResponseEncoder copy from http.DefaultResponseEncoder | |
func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{}) error { | |
if v == nil { | |
return nil | |
} | |
if rd, ok := v.(http.Redirector); ok { | |
url, code := rd.Redirect() | |
nt.Redirect(w, r, url, code) | |
return nil | |
} | |
codec := encoding.GetCodec(json.Name) // ignore Accept Header | |
data, err := codec.Marshal(v) | |
if err != nil { | |
return err | |
} | |
bs, _ := sj.Marshal(NewResponse(data)) | |
w.Header().Set("Content-Type", ContentType(codec.Name())) | |
_, err = w.Write(bs) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
// DefaultErrorEncoder copy from http.DefaultErrorEncoder. | |
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) { | |
se := FromError(errors.FromError(err)) // change error to BaseResponse | |
codec := encoding.GetCodec(json.Name) // ignore Accept header | |
body, err := codec.Marshal(se) | |
if err != nil { | |
w.WriteHeader(nt.StatusInternalServerError) | |
return | |
} | |
w.Header().Set("Content-Type", ContentType(codec.Name())) | |
// w.WriteHeader(int(se.Code)) // ignore http status code | |
_, _ = w.Write(body) | |
} | |
const ( | |
baseContentType = "application" | |
) | |
// ContentType returns the content-type with base prefix. | |
func ContentType(subtype string) string { | |
return strings.Join([]string{baseContentType, subtype}, "/") | |
} | |
func NewResponse(data []byte) BaseResponse { | |
return BaseResponse{ | |
Code: 0, | |
Message: "success", | |
Data: sj.RawMessage(data), | |
} | |
} | |
func FromError(e *errors.Error) *BaseResponse { | |
if e == nil { | |
return nil | |
} | |
return &BaseResponse{ | |
Code: e.Code, | |
Message: e.Message, | |
} | |
} | |
type BaseResponse struct { | |
Code int32 `json:"code"` | |
Message string `json:"message"` | |
Data sj.RawMessage `json:"data,omitempty"` | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
"sj" 是 standard json 的简写,代表 "encoding/json"。