Documentation
¶
Overview ¶
Package marshal provides helpers to write JSON HTTP responses and to decode JSON request bodies, using github.com/ugorji/go/codec.
WriteJSON is the single response path used by porto handlers: it writes errors (anything implementing WriteHTTPResponse, such as httperror.Error) with their own status code, and everything else as a 200 application/json body, gzip-compressed when it is at least 1 KiB and the client accepts gzip, and pretty-printed when the URL has a "?pp" query parameter. Success responses include Vary: Accept-Encoding.
func (s *svc) get(w http.ResponseWriter, r *http.Request, _ restserver.Params) {
var req ItemRequest
if marshal.DecodeBody(w, r, &req) != nil {
return // 400 invalid_json or 413 request_too_large already written
}
item, err := s.store.Get(r.Context(), req.ID)
marshal.WriteJSON(w, r, err, item) // first non-nil value wins
}
Decoding is strict: unknown JSON fields are an error (DecoderHandle). DecodeBody limits request bodies to 10 MiB by default; LimitRequestBody configures a different limit or explicitly disables it with a negative value.
Index ¶
- func AcceptsGzip(h http.Header) bool
- func Decode(r io.Reader, result any) error
- func DecodeBody(w http.ResponseWriter, r *http.Request, result any) error
- func DecodeBytes(data []byte, result any) error
- func DecoderHandle() *codec.JsonHandle
- func EncodeBytes(printSetting PrettyPrintSetting, value any) ([]byte, error)
- func LimitRequestBody(next http.Handler, maxBytes int64) http.Handler
- func NewEncoder(w io.Writer, r *http.Request) *codec.Encoder
- func NewRequest(method string, url string, req any) (*http.Request, error)
- func WriteJSON(w http.ResponseWriter, r *http.Request, bodies ...any)
- func WritePlainJSON(w http.ResponseWriter, statusCode int, body any, ...)
- type PrettyPrintSetting
- type WriteHTTPResponse
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AcceptsGzip ¶ added in v1.0.448
AcceptsGzip reports whether Accept-Encoding permits gzip, considering all header lines and quality values. An explicit gzip entry overrides a wildcard; malformed or out-of-range quality values are treated as a refusal.
func Decode ¶
Decode reads JSON from r and decodes it into result using DecoderHandle. The reader is not size-limited; wrap request bodies with http.MaxBytesReader first.
func DecodeBody ¶
DecodeBody decodes the JSON request body into result. On failure it writes a 400 invalid_json response, or 413 request_too_large on overflow, and returns the error, so callers can simply return. A known oversized Content-Length is rejected before any byte is read. The default limit is limits.DefaultMaxRequestBody; LimitRequestBody overrides it. After a successful decode, the remaining body is consumed within the limit so trailing bytes cannot bypass the size check; with the limit disabled it is left unread. Decode and DecodeBytes remain unbounded.
func DecodeBytes ¶
DecodeBytes decodes JSON data into result using DecoderHandle (strict: unknown fields are an error).
func DecoderHandle ¶
func DecoderHandle() *codec.JsonHandle
DecoderHandle returns the codec handle used for decoding JSON into Go types. It errors when a JSON field has no matching Go field, and decodes untyped objects into map[string]any. The returned handle is shared and must not be mutated by callers.
func EncodeBytes ¶
func EncodeBytes(printSetting PrettyPrintSetting, value any) ([]byte, error)
EncodeBytes encodes value to JSON with the given pretty-print setting and returns the bytes.
func LimitRequestBody ¶ added in v1.0.438
LimitRequestBody bounds reads from request bodies. Zero selects limits.DefaultMaxRequestBody; a negative value disables the limit. It writes no response itself, so it can wrap CORS, correlation, logging and metrics middleware without bypassing them: a body whose known Content-Length exceeds the limit fails on its first read with *http.MaxBytesError without reading any bytes, and other bodies fail once they exceed the limit. DecodeBody honors this policy and writes HTTP 413; other body readers must handle *http.MaxBytesError themselves.
func NewEncoder ¶
NewEncoder returns a JSON encoder writing to w, pretty-printing when the request URL has a "pp" query parameter. r must not be nil.
func NewRequest ¶
NewRequest builds an http.Request whose body is req: an io.Reader, []byte or string is sent as is, anything else is JSON-encoded with encoding/json. No Content-Type header is set.
func WriteJSON ¶
func WriteJSON(w http.ResponseWriter, r *http.Request, bodies ...any)
WriteJSON serialises the first non-nil body value as the HTTP response. A value implementing WriteHTTPResponse writes itself (httperror values set their own status); any other error is converted with httperror.NewFromPb (500 unexpected unless it carries a gRPC status) and written the same way, except that a converted error with a 5xx status is sent with the generic http.StatusText message while its text is only logged; errors other than 404 are also logged with the caller's file and line. Anything else is written as application/json with status 200, gzip-compressed for payloads of at least 1 KiB when the request accepts gzip, and pretty-printed when the URL has a "pp" query parameter. Success responses vary by Accept-Encoding. Encoding and write failures are logged, not reported. r must not be nil.
Passing several values lets a handler write either the error or the result in one call:
x, err := doSomething() marshal.WriteJSON(w, r, err, x)
func WritePlainJSON ¶
func WritePlainJSON(w http.ResponseWriter, statusCode int, body any, printSetting PrettyPrintSetting)
WritePlainJSON writes body as application/json with the given status code and pretty-print setting, without gzip, error handling or logging.
Types ¶
type PrettyPrintSetting ¶
type PrettyPrintSetting int
PrettyPrintSetting controls how to format json when encoding a go type -> json
const ( // DontPrettyPrint the output has no additional space/line breaks, and has random ordering of map keys DontPrettyPrint PrettyPrintSetting = 0 // PrettyPrint provides an output more suitable for human consumption, it has line breaks/indenting // and map keys are generated in order. PrettyPrint PrettyPrintSetting = 1 )
type WriteHTTPResponse ¶
type WriteHTTPResponse interface {
// WriteHTTPResponse writes the value, including status and headers, to w.
WriteHTTPResponse(w http.ResponseWriter, r *http.Request)
}
WriteHTTPResponse is implemented by types that take full control over how they are written as an HTTP response (httperror.Error and ManyError do).