You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kratos/transport/http/codec.go

91 lines
2.6 KiB

package http
import (
"io"
"net/http"
"github.com/go-kratos/kratos/v2/encoding"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/internal/httputil"
)
// SupportPackageIsVersion1 These constants should not be referenced from any other code.
const SupportPackageIsVersion1 = true
// DecodeRequestFunc is decode request func.
type DecodeRequestFunc func(*http.Request, interface{}) error
// EncodeResponseFunc is encode response func.
type EncodeResponseFunc func(http.ResponseWriter, *http.Request, interface{}) error
// EncodeErrorFunc is encode error func.
type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error)
// DefaultRequestDecoder decodes the request body to object.
func DefaultRequestDecoder(r *http.Request, v interface{}) error {
codec, ok := CodecForRequest(r, "Content-Type")
if !ok {
return errors.BadRequest("CODEC", r.Header.Get("Content-Type"))
}
data, err := io.ReadAll(r.Body)
if err != nil {
return errors.BadRequest("CODEC", err.Error())
}
if len(data) == 0 {
return nil
}
if err = codec.Unmarshal(data, v); err != nil {
return errors.BadRequest("CODEC", err.Error())
}
return nil
}
// DefaultResponseEncoder encodes the object to the HTTP response.
func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{}) error {
codec, _ := CodecForRequest(r, "Accept")
data, err := codec.Marshal(v)
if err != nil {
return err
}
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
if code, ok := v.(StatusCoder); ok {
w.WriteHeader(code.StatusCode())
}
_, err = w.Write(data)
if err != nil {
return err
}
return nil
}
// DefaultErrorEncoder encodes the error to the HTTP response.
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
se := errors.FromError(err)
codec, _ := CodecForRequest(r, "Accept")
body, err := codec.Marshal(se)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
w.WriteHeader(int(se.Code))
_, _ = w.Write(body)
}
// CodecForRequest get encoding.Codec via http.Request
func CodecForRequest(r *http.Request, name string) (encoding.Codec, bool) {
for _, accept := range r.Header[name] {
codec := encoding.GetCodec(httputil.ContentSubtype(accept))
if codec != nil {
return codec, true
}
}
return encoding.GetCodec("json"), false
}
// StatusCoder is checked by DefaultResponseEncoder. If a response value implements
// StatusCoder, the StatusCode will be used to set http status code.
type StatusCoder interface {
StatusCode() int
}