labstack / echo

High performance, minimalist Go web framework

Home Page:https://echo.labstack.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add an AllowContentType middleware (based on chi)

pzolo85 opened this issue · comments

commented

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

@pzolo85

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)
		}
	}
}