skip to Main Content

I am using Echo web framework in Golang and I wrote this code

package main

import (
    "github.com/labstack/echo/v4"
    "net/http"
)

type ProjectPath struct {
    ID string `param:"id"`
}

type ProjectBody struct {
    Name string `json:"name"`
}

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

    e.POST("/project/:id", getProjectHandler)

    e.Start(":8080")
}

func getProjectHandler(c echo.Context) error {
    path := new(ProjectPath)
    if err := c.Bind(path); err != nil {
        return err
    }

    body := new(ProjectBody)
    if err := c.Bind(body); err != nil {
        return err
    }

    // Access the fields separately
    projectID := path.ID
    projectName := body.Name

    // Do something with the path and body fields...

    // Return a response
    return c.String(http.StatusOK, "Project ID: "+projectID+", Project Name: "+projectName)
}

I am trying to bind a path param and json body separately in a post request but I am getting an EOF error while trying to run it.

I am using replit for testing and the server is running on: https://echotest.sandeepacharya.repl.co

Curl Post Request:

 curl -X POST https://echotest.sandeepacharya.repl.co/project/123 -H "Content-Type: application/json" -d '{"name":"Sandeep"}'

Response:

{"message":"EOF"}

2

Answers


  1. Bind() reads request body directly from the socket and once read it can’t read again, hence the EOF error

    https://github.com/labstack/echo/issues/782#issuecomment-317503513

    The issue is occured due to that you are attempted to bind the request body twice. The second c.Bind() will raise an error because the request body has already been read and consumed.

    if you want to read the request with different struct, you can follow this approach

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/labstack/echo/v4"
    )
    
    type ProjectPath struct {
        ID string `param:"id"`
    }
    
    type ProjectBody struct {
        Name string `json:"name"`
    }
    
    type ProjectRequest struct {
        ProjectPath
        ProjectBody
    }
    
    func main() {
        e := echo.New()
    
        e.POST("/project/:id", getProjectHandler)
    
        e.Start(":8080")
    }
    
    func getProjectHandler(c echo.Context) error {
    
        project := new(ProjectRequest)
        if err := c.Bind(project); err != nil {
            fmt.Println("Error happened due to::", err)
            return err
        }
    
        // Access the fields separately
        projectID := project.ID
        projectName := project.Name
    
        // Do something with the path and body fields...
    
        // Return a response
        return c.String(http.StatusOK, "Project ID: "+projectID+", Project Name: "+projectName)
    }
    
    

    or you can use only one struct to bind the request

    type ProjectRequest struct {
        Name string `json:"name"`
        ID string `param:"id"`
    }
    
    Login or Signup to reply.
  2. If you want to do the binding separately then you’ll need to use the source specific bind methods. These methods however are NOT available in the context, instead they are implemented by the DefaultBinder.

    See also: https://echo.labstack.com/guide/binding/#direct-source

    func getProjectHandler(c echo.Context) error {
        path := new(ProjectPath)
        if err := (&echo.DefaultBinder{}).BindPathParams(path); err != nil {
            return err
        }
    
        body := new(ProjectBody)
        if err := (&echo.DefaultBinder{}).BindBody(body); err != nil {
            return err
        }
    
        // Access the fields separately
        projectID := path.ID
        projectName := body.Name
    
        // Do something with the path and body fields...
    
        // Return a response
        return c.String(http.StatusOK, "Project ID: "+projectID+", Project Name: "+projectName)
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search