feat: support minimal prefer header

This commit is contained in:
Daniel G. Taylor 2020-04-04 13:46:51 -07:00
parent 652542346a
commit 09fe1265dd
No known key found for this signature in database
GPG key ID: 7BD6DC99C9A87E22
4 changed files with 74 additions and 0 deletions

View file

@ -29,6 +29,7 @@ Features include:
- Generates JSON Schema from Go types
- Automatic input model validation & error handling
- Dependency injection for loggers, datastores, etc
- Automatic handling of `Prefer: return=minimal` from [RFC 7240](https://tools.ietf.org/html/rfc7240#section-4.2)
- Documentation generation using [RapiDoc](https://mrin9.github.io/RapiDoc/), [ReDoc](https://github.com/Redocly/redoc), or [SwaggerUI](https://swagger.io/tools/swagger-ui/)
- CLI built-in, configured via arguments or environment variables
- Set via e.g. `-p 8000`, `--port=8000`, or `SERVICE_PORT=8000`
@ -574,6 +575,7 @@ g := gin.New()
g.Use(huma.Recovery())
g.Use(huma.LogMiddleware(nil, nil))
g.Use(cors.Default())
g.Use(huma.PreferMinimalMiddleware())
g.NoRoute(huma.Handler404)
r := huma.NewRouterWithGin(g, &huma.OpenAPI{
Title: "Notes API",

View file

@ -117,3 +117,38 @@ func Handler404(c *gin.Context) {
Message: "Not found",
})
}
type minimalWriter struct {
gin.ResponseWriter
w http.ResponseWriter
}
func (w *minimalWriter) Write(data []byte) (int, error) {
if w.Status() == http.StatusNoContent {
return 0, nil
}
return w.ResponseWriter.Write(data)
}
func (w *minimalWriter) WriteHeader(statusCode int) {
if statusCode >= 200 && statusCode < 300 {
statusCode = http.StatusNoContent
}
w.ResponseWriter.WriteHeader(statusCode)
}
// PreferMinimalMiddleware will remove the response body and return 204 No
// Content for any 2xx response where the request had the Prefer: return=minimal
// set on the request.
func PreferMinimalMiddleware() func(*gin.Context) {
return func(c *gin.Context) {
// Wrap the response writer
if c.GetHeader("prefer") == "return=minimal" {
c.Writer = &minimalWriter{ResponseWriter: c.Writer}
}
c.Next()
}
}

View file

@ -37,6 +37,42 @@ func TestRecoveryMiddleware(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.Result().Header.Get("content-type"))
}
func TestPreferMinimalMiddleware(t *testing.T) {
r := NewTestRouter(t)
r.GinEngine().Use(PreferMinimalMiddleware())
r.Resource("/test").Get("desc", func() string {
return "Hello, test"
})
r.Resource("/non200", ResponseText(http.StatusBadRequest, "desc")).Get("desc", func() string {
return "Error details"
})
// Normal request
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/test", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.NotEmpty(t, w.Body.String())
// Prefer minimal should return 204 No Content
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, "/test", nil)
req.Header.Add("prefer", "return=minimal")
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
// Prefer minimal which can still return non-200 response bodies
w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, "/non200", nil)
req.Header.Add("prefer", "return=minimal")
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
assert.NotEmpty(t, w.Body.String())
}
func TestHandler404(t *testing.T) {
g := gin.New()
g.NoRoute(Handler404)

View file

@ -218,6 +218,7 @@ func NewRouter(api *OpenAPI) *Router {
g.Use(Recovery())
g.Use(LogMiddleware(nil, nil))
g.Use(cors.Default())
g.Use(PreferMinimalMiddleware())
g.NoRoute(Handler404)
return NewRouterWithGin(g, api)
}