labstack / echo

High performance, minimalist Go web framework
https://echo.labstack.com
MIT License
29.81k stars 2.22k forks source link

How to implement endpoints for specific content-types #1584

Open eloo opened 4 years ago

eloo commented 4 years ago

Issue Description

How to implement endpoints for specific content-types

Checklist

Expected behaviour

I've have an PATCH endpoint which only accepts requests with content-type application/merge-patch+json

Actual behaviour

Not sure how to implement it

Details

I'm currently trying to create an endpoint which supports the https://tools.ietf.org/html/rfc7396 standard for patching resources. But im struggling right now how to implement the specific content-type i want to accept. So every request without the content-type to be set to application/merge-patch+json should be rejected with at 415.

My two ideas are the following:

I guess the second approach is the better but i'm not sure if there is maybe another solution or what should be prefered?

Thanks

mymtw commented 4 years ago

think that you should check both

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

eloo commented 4 years ago

for the record: i'm now using the following snippet for this use-case:

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"

    "github.com/labstack/echo/v4"
)

type MergeJsonBinder struct{}

// Bind implements the `Binder#Bind` function.
func (b *MergeJsonBinder) Bind(i interface{}, c echo.Context) (err error) {
    req := c.Request()

    names := c.ParamNames()
    values := c.ParamValues()
    params := map[string][]string{}
    for i, name := range names {
        params[name] = []string{values[i]}
    }
    if req.ContentLength == 0 {
        return
    }
    ctype := req.Header.Get(echo.HeaderContentType)
    switch {
    case strings.HasPrefix(ctype, MIMEApplicationMergePatchJSON):
        if err = json.NewDecoder(req.Body).Decode(i); err != nil {
            if ute, ok := err.(*json.UnmarshalTypeError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)).SetInternal(err)
            } else if se, ok := err.(*json.SyntaxError); ok {
                return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())).SetInternal(err)
            }
            return echo.NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
        }
    default:
        return echo.ErrUnsupportedMediaType
    }
    return
}

and something like this in the endpoint handler

mergeJsonBinder := MergeJsonBinder{}
        var body map[string]interface{}
        err := mergeJsonBinder.Bind(&body, c)
        if err != nil {
            return err
        }

maybe this can be adapted and added as an example to the documention?

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

lyda commented 9 months ago

I submitted PR #2572 which would fix this issue.