Skip to content

Instantly share code, notes, and snippets.

@kvii
Created October 18, 2023 19:55
Show Gist options
  • Save kvii/492b2fa7726500269476fcfbd6c668dc to your computer and use it in GitHub Desktop.
Save kvii/492b2fa7726500269476fcfbd6c668dc to your computer and use it in GitHub Desktop.
kratos http.Server with "code-msg-data" encoding.
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"`
}
@kvii
Copy link
Author

kvii commented Oct 18, 2023

"sj" 是 standard json 的简写,代表 "encoding/json"。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment