60 lines
1.4 KiB
Markdown
60 lines
1.4 KiB
Markdown
|
|
# httputil
|
||
|
|
|
||
|
|
Typed HTTP handler adapters and response helpers for stdlib `net/http`.
|
||
|
|
|
||
|
|
## Install
|
||
|
|
|
||
|
|
```
|
||
|
|
go get code.nochebuena.dev/go/httputil
|
||
|
|
```
|
||
|
|
|
||
|
|
## Typed handlers
|
||
|
|
|
||
|
|
```go
|
||
|
|
// JSON body + validation + typed response
|
||
|
|
r.Post("/orders", httputil.Handle(validator, svc.CreateOrder))
|
||
|
|
|
||
|
|
// No request body (GET / DELETE)
|
||
|
|
r.Get("/orders/{id}", httputil.HandleNoBody(svc.GetOrder))
|
||
|
|
|
||
|
|
// Request body, no response body (204)
|
||
|
|
r.Delete("/orders/{id}", httputil.HandleEmpty(validator, svc.DeleteOrder))
|
||
|
|
|
||
|
|
// Manual handler with centralised error mapping
|
||
|
|
r.Get("/raw", httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||
|
|
data, err := svc.Load(r.Context(), chi.URLParam(r, "id"))
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return httputil.JSON(w, http.StatusOK, data)
|
||
|
|
}))
|
||
|
|
```
|
||
|
|
|
||
|
|
## Error mapping
|
||
|
|
|
||
|
|
`xerrors.Code` → HTTP status:
|
||
|
|
|
||
|
|
| Code | Status |
|
||
|
|
|---|---|
|
||
|
|
| `ErrInvalidInput` | 400 |
|
||
|
|
| `ErrUnauthorized` | 401 |
|
||
|
|
| `ErrPermissionDenied` | 403 |
|
||
|
|
| `ErrNotFound` | 404 |
|
||
|
|
| `ErrAlreadyExists` | 409 |
|
||
|
|
| `ErrInternal` | 500 |
|
||
|
|
| `ErrNotImplemented` | 501 |
|
||
|
|
| `ErrUnavailable` | 503 |
|
||
|
|
| `ErrDeadlineExceeded` | 504 |
|
||
|
|
| unknown | 500 |
|
||
|
|
|
||
|
|
Error response body:
|
||
|
|
```json
|
||
|
|
{"code": "NOT_FOUND", "message": "record not found"}
|
||
|
|
```
|
||
|
|
Fields from `xerrors.Err` are merged into the top-level response object.
|
||
|
|
|
||
|
|
## Dependencies
|
||
|
|
|
||
|
|
- `code.nochebuena.dev/go/xerrors`
|
||
|
|
- `code.nochebuena.dev/go/valid`
|