hotmeatballsoup
hotmeatballsoup

Reputation: 625

Golang Chi router not rendering response payload JSON

Go here using Chi renderer for a basic REST service. I have the following structs and functions:

type Order struct {
    OrderId      uuid.UUID         `json:"orderId",gorm:"type:uuid;primary_key;not null;default gen_random_uuid()"`
    Quantity     int               `json:"quantity",gorm:"not null"`
    Status       string            `json:"status",gorm:"not null"`
}

func (o *Order) Bind(r *http.Request) error {
    return nil
}

func (o *Order) Render(w http.ResponseWriter, r *http.Request) error {
    return nil
}

func NewOrdersList(orders []Order) []render.Renderer {
    list := []render.Renderer{}
    for _, order := range orders {
        list = append(list, &order)
    }
    return list
}

func GetOrderByIdHandler() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {

        orderId := chi.URLParam(r, "orderId")

        order, err := fetchOrder(orderId)
        if err != nil {
            render.Render(w, r, NewInternalServerError(err))
            return
        }

    log.Info("order status is " + order.Status)

        render.Bind(r, &order)
        return
    }
}

When I run this and hit the endpoint that invokes the GetOrderByIdHandler() function, I get back a 200 OK/Success. However there is no JSON in the response body, whereas I would have expected a marshalled JSON payload representing an "order", such as:

{
  "orderId": "12345",
  "quantity": 1,
  "status": "SENT"
}

However my curl shows nothing in the response body:

$ curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X GET http://localhost:9400/myapp/v1/orders/12345
HTTP/1.1 200 OK
Vary: Origin
Date: Wed, 24 Jun 2020 07:09:30 GMT
Content-Length: 0

Any idea where I'm going awry? I do see the log statement print out the order status right before calling bind, so I know its not a null/empty order instance.

Upvotes: 4

Views: 12649

Answers (1)

mkopriva
mkopriva

Reputation: 38243

render.Bind is input-only, i.e. for decoding the request payload. Instead use render.JSON to send a json response.

func GetOrderByIdHandler() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        orderId := chi.URLParam(r, "orderId")
        order, err := fetchOrder(orderId)
        if err != nil {
            render.Render(w, r, NewInternalServerError(err))
            return
        }

        log.Info("order status is " + order.Status)
        render.JSON(w, r, order)
    }
}

Or, alternatively, you could also use the standard approach: import the encoding/json package and then use it like so:

func GetOrderByIdHandler() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        orderId := chi.URLParam(r, "orderId")
        order, err := fetchOrder(orderId)
        if err != nil {
            render.Render(w, r, NewInternalServerError(err))
            return
        }

        log.Info("order status is " + order.Status)

        w.Header().Set("Content-Type", "application/json")
        if err := json.NewEncoder(w).Encode(order); err != nil {
            render.Render(w, r, NewInternalServerError(err))
        }
    }
}

Also note that the proper format for multiple struct tags is "space delimited" not "comma separated". For example: json:"quantity" gorm:"not null" is correct, while json:"quantity",gorm:"not null" is not.

Upvotes: 4

Related Questions