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

Feature request: support Yaml responses transparently. #2594

Open
hsyed opened this issue Feb 14, 2024 · 4 comments
Open

Feature request: support Yaml responses transparently. #2594

hsyed opened this issue Feb 14, 2024 · 4 comments
Labels

Comments

@hsyed
Copy link

hsyed commented Feb 14, 2024

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
Copy link
Contributor

aldas commented Feb 14, 2024

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

@aldas aldas added the feature label Mar 9, 2024
@ravilock
Copy link

ravilock commented Jan 3, 2025

Hey @aldas question on this line:

but adding c.Yaml would be API breaking change.

Would that be a breaking change? IMHO it sounds like an extension of the current API but not a breaking change to it. I might be wrong and in some particular use case, even just extending the API with new methods would result on a breaking change for someone.

@aldas
Copy link
Contributor

aldas commented Jan 3, 2025

In current version of Echo echo.Context is an interface. Adding method to interface is a breaking change as any struct that had previously satisfied echo.Context interface will no longer do that and fail assignments or when passed as an argument.

@aldas
Copy link
Contributor

aldas commented Jan 3, 2025

Try this https://go.dev/play/p/U067fl1yfXu

package main

import "fmt"

type EchoContext interface {
	Method1() string
	//Method2() string // <----------- uncomment this and line 19 will not compile anymore
}

type MyContext struct{}

func (c *MyContext) Method1() string {
	return "hello world"
}

func main() {
	var c EchoContext
	c = &MyContext{}  // This line
	fmt.Println(c.Method1())
}

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

No branches or pull requests

3 participants