labstack / echo

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

Feature request: support Yaml responses transparently. #2594

Open hsyed opened 4 months ago

hsyed commented 4 months ago

I used echo as an API gateway in front of some kubernetes resources, being able to return responses rendered as Yaml would have been handy.

Kubernetes API machinery produces Yaml off of the Json tags. Could we do something like that here ? The API wouldn't have to change.

Supporting it similarly on the request side (Bind) would also be great but perhaps for another issue.

This issue is related.

aldas commented 4 months ago

Sending responses in Echo is 1 method call on context ala c.JSON(http.StatusOK, data). If you create utility method for sending yaml response it will be same.

func Yaml(c echo.Context, code int, i interface{}) error {
    c.Response().Status = code
    c.Response().Header().Set(echo.HeaderContentType, "text/yaml")
    return yaml.NewEncoder(c.Response()).Encode(i)
}

and you would use that as return Yaml(c, http.StatusCreated, payload) which is not much different from return c.Yaml(http.StatusCreated, payload)

but adding c.Yaml would be API breaking change.


full example:

import (
    "errors"
    "github.com/labstack/echo/v4"
    "gopkg.in/yaml.v3"
    "log"
    "net/http"
)

// curl -v -X POST http://localhost:8080/yaml -H 'Content-Type: application/json' -d '{"name":"test","data":{"x":true}}'
func Yaml(c echo.Context, code int, i interface{}) error {
    c.Response().Status = code
    c.Response().Header().Set(echo.HeaderContentType, "text/yaml")
    return yaml.NewEncoder(c.Response()).Encode(i)
}

type data struct {
    Name string                 `yaml:"name" json:"name"`
    Data map[string]interface{} `yaml:"data" json:"data"`
}

func main() {
    e := echo.New()

    e.POST("/yaml", func(c echo.Context) error {
        var payload data
        if err := c.Bind(&payload); err != nil {
            return err
        }
        return Yaml(c, http.StatusCreated, payload)
    })

    if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
        log.Fatal(err)
    }
}
curl -v -X POST http://localhost:8080/yaml -H 'Content-Type: application/json' -d '{"name":"test","data":{"x":true}}'

Output:

x@x:~/$ curl -v -X POST http://localhost:8080/yaml -H 'Content-Type: application/json' -d '{"name":"test","data":{"x":true}}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* processing: http://localhost:8080/yaml
*   Trying [::1]:8080...
* Connected to localhost (::1) port 8080
> POST /yaml HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.2.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 33
> 
< HTTP/1.1 201 Created
< Content-Type: text/yaml
< Date: Wed, 14 Feb 2024 18:08:41 GMT
< Content-Length: 29
< 
name: test
data:
    x: true
* Connection #0 to host localhost left intact