Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an AllowContentType middleware (based on chi) #2551

Open
pzolo85 opened this issue Nov 28, 2023 · 2 comments
Open

Add an AllowContentType middleware (based on chi) #2551

pzolo85 opened this issue Nov 28, 2023 · 2 comments

Comments

@pzolo85
Copy link

pzolo85 commented Nov 28, 2023

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

@jub0bs
Copy link

jub0bs commented Apr 1, 2024

@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?

@aldas
Copy link
Contributor

aldas commented Apr 6, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants