HTTP Middleware for Request Body Limits
Cap JSON request bodies with MaxBytesReader, reject oversized payloads with 413, and reuse the middleware across handlers.
Code
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
)
type Middleware func(http.Handler) http.Handler
type Payload struct {
Name string `json:"name"`
}
func LimitBody(maxBytes int64) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxBytes)
next.ServeHTTP(w, r)
})
}
}
func decodePayload(w http.ResponseWriter, r *http.Request) (Payload, error) {
var payload Payload
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(&payload); err != nil {
var tooLarge *http.MaxBytesError
if errors.As(err, &tooLarge) {
http.Error(w, fmt.Sprintf("body exceeds %d bytes", tooLarge.Limit), http.StatusRequestEntityTooLarge)
return payload, err
}
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return payload, err
}
if strings.TrimSpace(payload.Name) == "" {
http.Error(w, "name is required", http.StatusUnprocessableEntity)
return payload, errors.New("name is required")
}
return payload, nil
}
func main() {
handler := LimitBody(64)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
payload, err := decodePayload(w, r)
if err != nil {
return
}
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, "created %s\n", payload.Name)
}))
bodies := []string{
`{"name":"ada"}`,
`{"name":"` + strings.Repeat("x", 80) + `"}`,
}
for _, body := range bodies {
request := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(body))
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
fmt.Printf("status=%d body=%q\n", recorder.Code, strings.TrimSpace(recorder.Body.String()))
}
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.