Add an AllowContentType middleware (based on chi)
pzolo85 opened this issue · comments
Add an AllowContentType middleware
Checklist
- [ x] Dependencies installed
- [ x] No typos
- [ x] Searched existing issues and docs
Expected behaviour
This is a very handy middleware available in Chi. I thought it would be a good idea to have one in echo.
Actual behaviour
N/A
Steps to reproduce
N/A
Working code to debug
func AllowContentType(contentTypes ...string) echo.MiddlewareFunc {
if len(contentTypes) == 0 {
panic("echo: allow-content middleware requires a valida content-type")
}
allowedContentTypes := make(map[string]struct{}, len(contentTypes))
for _, ctype := range contentTypes {
allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{}
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if c.Request().ContentLength == 0 {
// skip check for empty content body
return next(c)
}
s := strings.ToLower(strings.TrimSpace(c.Request().Header.Get("Content-Type")))
if i := strings.Index(s, ";"); i > -1 {
s = s[0:i]
}
if _, ok := allowedContentTypes[s]; ok {
return next(c)
}
return echo.NewHTTPError(http.StatusUnsupportedMediaType)
}
}
}
Version/commit
s := strings.ToLower(strings.TrimSpace(c.Request().Header.Get("Content-Type")))
if i := strings.Index(s, ";"); i > -1 {
s = s[0:i]
}
Why not rely on https://pkg.go.dev/mime#ParseMediaType
instead?
I do not know if this makes sense. If we leave out usual middleware boilerplate what you need is
func AllowContentType(contentTypes ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
mediaType, _, err := mime.ParseMediaType(c.Request().Header.Get("Content-Type"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid content-type value")
}
if slices.Contains(contentTypes, mediaType) {
return next(c)
}
return echo.NewHTTPError(http.StatusUnsupportedMediaType)
}
}
}
so it is only mime.ParseMediaType
+ slices.Contains
calls + error handling. Over time other people would add their special cases and the middleware "useful" code percentage will fall and code serving edge cases goes up.
It is not maybe that complex use case to build middleware for it.
Full example:
/*
curl --location 'http://localhost:8080/example' -d 'name=test'
*/
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(AllowContentType("application/x-www-form-urlencoded"))
e.POST("/example", func(c echo.Context) error {
return c.String(http.StatusOK, c.Path())
})
if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}
func AllowContentType(contentTypes ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
mediaType, _, err := mime.ParseMediaType(c.Request().Header.Get("Content-Type"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid content-type value")
}
if slices.Contains(contentTypes, mediaType) {
return next(c)
}
return echo.NewHTTPError(http.StatusUnsupportedMediaType)
}
}
}