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 // 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 } // Redirector replies to the request with a redirect to url // which may be a path relative to the request path. type Redirector interface { Redirect() (string, int) } // Request type net/http. type Request = http.Request // ResponseWriter type net/http. type ResponseWriter = http.ResponseWriter // 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 { if v == nil { _, err := w.Write(nil) return err } codec, _ := CodecForRequest(r, "Accept") data, err := codec.Marshal(v) if err != nil { return err } if rd, ok := v.(Redirector); ok { url, code := rd.Redirect() http.Redirect(w, r, url, code) return nil } 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 }