Valhala
Valhala

Reputation: 21

Go gin nested JSON request body POST, error unexpected end of JSON input

I am new to GO and was trying to create a simple POST API with gin and gorm.

The request data is nested JSON like below:

{
  "fall_orders_request": [
    {
      "fruit": "Watermelon",
      "vegetable": "Carrot"
    }
  ],
  "spring_orders_request": [
    {
      "fruit": "Watermelon",
      "vegetable": "Carrot",
      "cupcake": "minions"
    }
  ],
  "custome_rates": [
    {
      "fruit": "Watermelon",
      "greentea": "Japanese",
      "cupcake": "pokemon"
    }
  ]
}

After receiving the request i.e orders the backend will save it to corresponding Databases for each session.

This is my code for the orders.go:

package order

import (
    "net/http"

    "github.com/gin-gonic/gin"

    "gorm.io/gorm"
)

type FallOrders struct {
    ID        uint   `gorm:"primarykey"`
    Fruit     string `json:"fruit"`
    Vegetable string `json:"vegetable"`
}

type SpringOrders struct {
    ID        uint   `gorm:"primarykey"`
    Fruit     string `json:"fruit"`
    Vegetable string `json:"vegetable"`
    Cupcake   string `json:"cupcake"`
}

type WinterOrders struct {
    ID       uint   `gorm:"primarykey"`
    Fruit    string `json:"fruit"`
    Greentea string `json:"greentea"`
    Cupcake  string `json:"cupcake"`
}

type allOrders struct {
    FallOrders   []FallOrders   `json:"fall_orders"`
    SpringOrders []SpringOrders `json:"spring_orders"`
    WinterOrders []WinterOrders `json:"winter_orders"`
}

type FallOrdersRequest struct {
    Fruit     string `json:"fruit"`
    Vegetable string `json:"vegetable"`
}

type SpringOrdersRequest struct {
    Fruit     string `json:"fruit"`
    Vegetable string `json:"vegetable"`
    Cupcake   string `json:"cupcake"`
}

type WinterOrdersRequest struct {
    Fruit    string `json:"fruit"`
    Greentea string `json:"greentea"`
    Cupcake  string `json:"cupcake"`
}

type AllOrdersRequest struct {
    FallOrdersRequest   []FallOrdersRequest   `json:"fall_orders_request"`
    SpringOrdersRequest []SpringOrdersRequest `json:"spring_orders_request"`
    WinterOrdersRequest []WinterOrdersRequest `json:"winter_orders_request"`
}

type AllOrdersManager struct {
    DB *gorm.DB
}

type FallOrdersManager struct {
    DB *gorm.DB
}

type SpringOrdersManager struct {
    DB *gorm.DB
}

type WinterOrdersManager struct {
    DB *gorm.DB
}

func CreateModularRates() gin.HandlerFunc {
    return func(c *gin.Context) {
        var aor AllOrdersRequest
        var form FallOrdersManager
        var sorm SpringOrdersManager
        var worm WinterOrdersManager

        if err := c.BindJSON(&aor); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        for _, fall := range aor.FallOrdersRequest {

            fallOrders := FallOrders{
                Fruit:     fall.Fruit,
                Vegetable: fall.Vegetable,
            }
            c.JSON(http.StatusCreated, fallOrders)
            res := form.DB.Create(&fallOrders)
            if res.Error != nil {
                return
            }
        }

        for _, spring := range aor.SpringOrdersRequest {

            springOrders := SpringOrders{
                Fruit:     spring.Fruit,
                Vegetable: spring.Vegetable,
                Cupcake:   spring.Cupcake,
            }
            c.JSON(http.StatusCreated, springOrders)
            res := sorm.DB.Create(&springOrders)
            if res.Error != nil {
                return
            }
        }

        for _, winter := range aor.WinterOrdersRequest {

            winterOrders := WinterOrders{
                Fruit:    winter.Fruit,
                Greentea: winter.Greentea,
                Cupcake:  winter.Cupcake,
            }
            c.JSON(http.StatusCreated, winterOrders)
            res := worm.DB.Create(&winterOrders)
            if res.Error != nil {
                return
            }
        }

    }
}

And this is the automated test orders_test.go

package order

import (
    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/gin-gonic/gin"
    "github.com/stretchr/testify/assert"
)

func TestOrderData() order.AllOrdersRequest {
    fall_orders_request := []order.FallOrdersRequest{}
    spring_orders_request := []order.SpringOrdersRequest{}
    winter_orders_request := []order.WinterOrdersRequest{}
    fall_orders_request = append(fall_orders_request, order.FallOrdersRequest{
        Fruit:     "Watermelon",
        Vegetable: "Carrot",
    })
    spring_orders_request = append(spring_orders_request, order.spring_orders_request{
        Fruit:     "Watermelon",
        Vegetable: "Carrot",
        Cupcake:   "minions",
    })
    winter_orders_request = append(winter_orders_request, order.winter_orders_request{
        Fruit:    "Watermelon",
        Greentea: "Japanese",
        Cupcake:  "pokemon",
    })

    return order.AllOrdersRequest{
        fall_orders_request:   fall_orders_request,
        spring_orders_request: spring_orders_request,
        winter_orders_request: winter_orders_request,
    }
}

func TestOrderCreation(t *testing.T) {
    params := TestOrderData()

    jsonPayload, _ := json.Marshal(params)
    w := httptest.NewRecorder()
    req, _ := http.NewRequest(
        "POST",
        "/orders",
        bytes.NewReader(jsonPayload),
    )
    var c *gin.Context
    assert.Equal(t, 201, w.Code)
    err2 := c.ShouldBindJSON(&req)
    if err2 == nil {
        return
    }

}

After running the test I get the following error:

Error: unexpected end of JSON input
{"message":"Error #01: EOF\n"}

Logging the request shows the request body is JSON as expected but not sure why I am getting this error.

Upvotes: 0

Views: 2459

Answers (1)

Chandan
Chandan

Reputation: 11797

If you are already in the order package you don't need it to reference it in each place you can directly access the method defined in the order package

Here struct name are incorrect order.spring_orders_request, order.winter_orders_request it should be order.SpringOrdersRequest, order.WinterOrdersRequest

spring_orders_request = append(spring_orders_request, order.spring_orders_request{
    Fruit:     "Watermelon",
    Vegetable: "Carrot",
    Cupcake:   "minions",
})
winter_orders_request = append(winter_orders_request, order.winter_orders_request{
    Fruit:    "Watermelon",
    Greentea: "Japanese",
    Cupcake:  "pokemon",
})

The key here are wrong is fall_orders_request, spring_orders_request, winter_orders_request it should be FallOrdersRequest, SpringOrdersRequest, WinterOrdersRequest

return order.AllOrdersRequest{
       fall_orders_request:   fall_orders_request,
       spring_orders_request: spring_orders_request,
       winter_orders_request: winter_orders_request,
}

Final:

package order

import (
    "fmt"

    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"

    // "github.com/gin-gonic/gin"
    "github.com/stretchr/testify/assert"
)

func OrderData() AllOrdersRequest {
    fall_orders_request := []FallOrdersRequest{}
    spring_orders_request := []SpringOrdersRequest{}
    winter_orders_request := []WinterOrdersRequest{}
    fall_orders_request = append(fall_orders_request, FallOrdersRequest{
        Fruit:     "Watermelon",
        Vegetable: "Carrot",
    })
    spring_orders_request = append(spring_orders_request, SpringOrdersRequest{
        Fruit:     "Watermelon",
        Vegetable: "Carrot",
        Cupcake:   "minions",
    })
    winter_orders_request = append(winter_orders_request, WinterOrdersRequest{
        Fruit:    "Watermelon",
        Greentea: "Japanese",
        Cupcake:  "pokemon",
    })

    return AllOrdersRequest{
        FallOrdersRequest:   fall_orders_request,
        SpringOrdersRequest: spring_orders_request,
        WinterOrdersRequest: winter_orders_request,
    }
}

func TestOrderCreation(t *testing.T) {
    params := OrderData()

    jsonPayload, _ := json.Marshal(params)
    // fmt.Println(jsonPayload)
    _bytes := bytes.NewReader(jsonPayload)

    // default `ResponseRecorder` `http.Response` status is 200
    // we need to update it to 201 before we access it in `assert`
    w := httptest.NewRecorder()
    w.WriteHeader(201)
    contentLength, err := w.Write(jsonPayload)
    fmt.Println(contentLength, err)
    req, _ := http.NewRequest(
        "POST",
        "http://localhost:8080/orders",
        _bytes,
    )
    assert.Equal(t, 201, w.Code)
    res := w.Result()
    fmt.Println(req)
    fmt.Println(res)
    // Not sure what you are trying to do here but since there is nothing
    // in the context and req variable is already defined of `http.Request` type
    // below statements doesn't make sense.
    // var c *gin.Context
    // if err := c.ShouldBindJSON(&req); err != nil {
    //     return
    // }
}

Upvotes: 0

Related Questions