'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.
Solution 1:[1]
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
// }
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
