package httpx import ( "encoding/xml" "fmt" "net/http" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" "git.yuntuo.hk/k2safe/go-omni/response" ) // SetResponseEncoder overrides this request's success AND error encoding. A // cloned context inherits it, including buffered/idempotent requests. func (c *Context) SetResponseEncoder(encoder response.Encoder) { c.encoder = encoder } // WithResponse applies a policy to one route/group. It deliberately stays set // until the outer error boundary finishes handling this request. func WithResponse(encoder response.Encoder) Middleware { return func(next Handler) Handler { return func(c *Context) error { c.SetResponseEncoder(encoder); return next(c) } } } // ResponseRegistrar installs the response policy before application middleware, // so early auth/protection failures and buffered clones use the route contract. type ResponseRegistrar interface { HandleWithResponse(string, string, response.Encoder, Handler, ...Middleware) } // HandleWithResponse uses the registrar's early policy hook when available. // Custom registrars without this hook get an ordinary leading route middleware. func HandleWithResponse(router interface { Handle(string, string, Handler, ...Middleware) }, method, path string, encoder response.Encoder, handler Handler, middleware ...Middleware) { if r, ok := router.(ResponseRegistrar); ok { r.HandleWithResponse(method, path, encoder, handler, middleware...) return } all := append([]Middleware{WithResponse(encoder)}, middleware...) router.Handle(method, path, handler, all...) } func (c *Context) writeResult(result response.Result) (err error) { if c.responded { return nil } defer func() { if recovered := recover(); recovered != nil { err = apierrors.Internal(fmt.Errorf("response encoder panic: %v", recovered)) } }() encoder := c.encoder if encoder == nil { encoder = response.Default() } payload, err := encoder.Encode(result) if err != nil { return apierrors.Internal(err) } if payload.Status < 200 || payload.Status > 599 { return apierrors.Internal(fmt.Errorf("response encoder returned invalid status %d", payload.Status)) } if (payload.Status == 204 || payload.Status == 304) && len(payload.Body) > 0 { return apierrors.Internal(fmt.Errorf("response encoder returned a body for status %d", payload.Status)) } return c.WriteResponse(payload.Status, payload.Header, payload.Body) } // Bytes, Text and XML are explicit wire responses; they bypass any envelope. func (c *Context) Bytes(status int, contentType string, body []byte) error { return c.WriteResponse(status, http.Header{"Content-Type": {contentType}}, body) } func (c *Context) Text(status int, body string) error { return c.Bytes(status, "text/plain; charset=utf-8", []byte(body)) } func (c *Context) XML(status int, value any) error { if c.responded { return nil } body, err := xml.Marshal(value) if err != nil { return apierrors.Internal(err) } return c.Bytes(status, "application/xml; charset=utf-8", body) }