package http import ( "bytes" "fmt" "io" "io/ioutil" "net/http" "net/url" "github.com/go-kratos/kratos/v2/encoding" "github.com/go-kratos/kratos/v2/errors" "github.com/go-kratos/kratos/v2/internal/httputil" "github.com/go-kratos/kratos/v2/transport/http/binding" "github.com/gorilla/mux" ) // SupportPackageIsVersion1 These constants should not be referenced from any other code. const SupportPackageIsVersion1 = true // 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 // Flusher type net/http type Flusher = http.Flusher // 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) // DefaultRequestVars decodes the request vars to object. func DefaultRequestVars(r *http.Request, v interface{}) error { raws := mux.Vars(r) vars := make(url.Values, len(raws)) for k, v := range raws { vars[k] = []string{v} } return binding.BindQuery(vars, v) } // DefaultRequestQuery decodes the request vars to object. func DefaultRequestQuery(r *http.Request, v interface{}) error { return binding.BindQuery(r.URL.Query(), v) } // 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", fmt.Sprintf("unregister Content-Type: %s", r.Header.Get("Content-Type"))) } data, err := io.ReadAll(r.Body) // reset body. r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) 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", fmt.Sprintf("body unmarshal %s", 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 { return nil } if rd, ok := v.(Redirector); ok { url, code := rd.Redirect() http.Redirect(w, r, url, code) return nil } codec, _ := CodecForRequest(r, "Accept") data, err := codec.Marshal(v) if err != nil { return err } w.Header().Set("Content-Type", httputil.ContentType(codec.Name())) _, 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 }