This question already has an answer here:
Unmarshalling json in golang
(1 answer)
Closed 4 years ago.
I have an API which accepts a request body like this:
The content-type is application/json.
{
"firstname": "foo",
"surname": "bar",
"age": 10,
"group":"test"
}
The request goes through when I use a client like Postman.
However, the same request fails from Go:
type Student struct {
firstname string
surname string
age int
group string
}
student:= Student{"foo", "bar", 10, "test"}
b, err := json.Marshal(student)
rest := restCall("POST", "http://api", b, "xyz123")
func restCall(method string, url string, body []byte, token string) {
req, _ := http.NewRequest(method, url, bytes.NewReader(body))
req.Header.Add("Authorization", token)
req.Header.Add("content-type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
}
I get an internal HTTP 500 error stating :
Mandatory parameter 'group' is missing.
But, I did pass it as you can see from my code.
Your code has several issues:
1. Structure must have public fields and fields should be tagged. Then it will be serialized properly:
type Student struct {
Firstname string `json:"firstname"`
Surname string `json: "surname"`
Age int . `json: "age"`
Group string `json: "group"`
}
2. Never ignore errors returned by method call. So you will not miss issues in your code:
b, err := json.Marshal(student)
if err != nil {
fmt.Println(err)
}
And the same after calls of NewRequest and DefaultClient.Do
Related
I am using this library in golang to connect to socket server.https://github.com/hesh915/go-socket.io-client I have to pass an auth object as server is expecting an object which will authenticate the user, but in this implementation, query seems to take map as a parameter. I get an EOF error while sending the stringified struct to Query as given below:
opts.Query["auth"] = str
I have created a nested structure for that purpose, tried converting struct into byte data using marshal and type casted into string, passed it as key value pair to map but that didn't seem to work out. Here is how my auth nested struct looks like:
{"userInfo":{"id":"","username":"","permissionsInfo":{"permissions"["",""]}},"additionalInfo":""}
How to send this information with the implementation given below:
opts := &socketio_client.Options{
Transport: "websocket",
Query: make(map[string]string),
}
opts.Query["user"] = "user"
opts.Query["pwd"] = "pass"
uri := "http://192.168.1.70:9090/socket.io/"
I have provided the link to the library.
Minimal Reproducible Example
type User struct {
Userinfo UserInfo `json:"userInfo"`
AdditionalInfo string `json:"additionalInfo"`
}
type UserInfo struct {
Id string `json:"id"`
Username string `json:"username"`
Permissionsinfo PermissionsInfo `json:"permissionInfo"`
}
type PermissionsInfo struct {
Permissions []string `json:"permissions"`
}
func main() {
user := &User{
Userinfo: UserInfo{
Id: "",
Username: "",
Permissionsinfo: PermissionsInfo{
Permissions: []string{"", ""},
}},
AdditionalInfo: "",
}
b, _ := json.Marshal(user)
fmt.Println(string(b))
opts := &socketio_client.Options{
Transport: "websocket",
Query: make(map[string]string),
}
opts.Query["auth"] = string(b)
uri := origin + "/socket.io"
client, err := socketio_client.NewClient(uri, opts)
if err != nil {
if err == io.EOF {
fmt.Printf("NewClient error:%v\n", err)
return
}
return
}
client.On("error", func() {
fmt.Println("on error")
})
}
The error to the above code, that i'm getting:
NewClient error EOF
My server end has to receive the auth params as socket.handshake.auth and that's understandable because the above functionality doesn't supports for auth instead it does for query. How can I achieve this functionality? Any help would be appreciated.
I am trying to ensure the body of a post request for example contains exact structure of the body and if not ahould throw an error
for example i have the following function
func UpdatePassword(c *fiber.Ctx) error {
type UpdatePasswordData struct {
Password string `json:"password" form:"password"`
NewPassword string `json:"new_password" form:"new_password"`
NewPasswordConfirm string `json:"new_password_confirm" form:"new_password_confirm"`
}
data := UpdatePasswordData{}
if err := c.BodyParser(&data); err != nil {
return err
}
var user models.User
if data.NewPassword != data.NewPasswordConfirm {
c.Status(400)
return c.JSON(fiber.Map{
"message": "passwords do not match",
})
}
email, _ := middlewares.GetUserEmail(c)
newPassword := models.HashPassword(data.NewPassword)
database.DB.Model(&user).Select("Password").Where("email = ?", email).Updates(map[string]interface{}{"Password": newPassword})
return c.JSON(user)
}
the POST request should be looking for body with this structure
{
"password": "oldpassword",
"new_password": "newpassword",
"new_password_confirm": "newpassword",
}
but currently this endpoint accepts body that does not have this exact structure. So how do i enforce the structure in the body of request, so that if structure does not match, i throw an error?
do not like gin, fiber has not builtin validate package
use go-playground/validator
go get github.com/go-playground/validator
example
type UpdatePasswordData struct {
Password string `json:"password" validate:"required,min=8,max=32"`
NewPassword string `json:"new_password" validate:"required,min=8,max=32"`
NewPasswordConfirm string `json:"new_password_confirm" validate:"eqfield=NewPassword"`
}
func UpdatePassword(c *fiber.Ctx) error {
var body UpdatePasswordData
if err := c.BodyParser(&body); err != nil {
return err
}
validate := validator.New()
if err := validate.Struct(body); err != nil {
return err
}
// do others
// get current user, check password == hash(body.password)
// save new passworld
}
or you can see fiber office docs https://docs.gofiber.io/guide/validation#validator-package
We can use struct tag
`validate:"required"`
to ensure that all the mandatory fields are there in the request payload.
Moreover we can validate the fields with the provided tags of the validator package and for additional validations we can implement custom validators and register them like this:
validate := validator.New()
validate.RegisterValidation("password-validator", PasswordValidator)
This question already has answers here:
json.Marshal(struct) returns "{}"
(3 answers)
Closed 11 months ago.
Consider the following code in main/entry function
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Post("/book", controllers.CreateBook)
http.ListenAndServe(":3333", r)
and CreateBook Function defined as
func CreateBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
var bookObj models.Book
err := json.NewDecoder(r.Body).Decode(&bookObj)
spew.Dump(bookObj)
collection := db.Client.Database("bookdb").Collection("book")
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
insertResult, err := collection.InsertOne(ctx, bookObj)
if err != nil {
log.Fatal(err)
}
json.NewEncoder(w).Encode(insertResult)
}
Book Model
//exporting attributes here solved the issue
type Book struct {
ID primitive.ObjectID `json:"id,omitempty" bson:"id,omitempty"`
name string `json:"name,omitempty" bson:"name,omitempty"`
author string `json:"author,omitempty" bson:"author,omitempty"`
isbn string `json:"isbn,omitempty" bson:"isbn,omitempty"`
}
However json.NewDecoder(r.Body).Decode(&bookObj) does not parse anything as req.Body is empty, no error thrown, it's about chi's Render function.
Can anyone help me to disable Render and Bind functions of chi, I would like to parse the body through JSON decoders only.
Exporting all fields of structs resolved the issue. thanks #mkopriva
type Book struct {
ID primitive.ObjectID `json:"id,omitempty" bson:"id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Author string `json:"author,omitempty" bson:"author,omitempty"`
ISBN string `json:"isbn,omitempty" bson:"isbn,omitempty"`
}
I'm a bit new to Go and I'm having trouble converting a response object from an API call into different structures based on the request type
Essentially, I have one func which sends out a request
func (fpc *FPClient) request(path string, method string, params interface{}, Token string, response interface{}) *dto.AppError {
client := &http.Client{
Timeout: time.Second * 15,
}
requestBody, err := json.Marshal(params)
if err != nil {
//
}
req, _ := http.NewRequest(method, path, bytes.NewBuffer(requestBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Cookie", fmt.Sprintf("cookie=%s;", Token))
req.SetBasicAuth(fpc.username, fpc.password)
resp, err := client.Do(req)
if err != nil {
//
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
//
}
if FPErr := fpc.processErrors(resp, body); FPerr != nil {
return FPErr
}
responseData := FPApiSuccess{Head: response, Body: response}
if err := json.Unmarshal(body, &responseData); err != nil {
//
}
fmt.Printf("\n\n client Response : \n %+v \n", responseData.Body)
return nil
}
The struct for FPApiSuccess is:
type FPApiSuccess struct {
Body interface{} `json:"body"`
Head interface{} `json:"head"`
}
Right now, there are 2 calling functions and the API response expected is a bit different for both
Both API responses have the below structure
{
"head": {},
"body": {}
}
However, the nested details in each key is different based on the API used.
I want to capture the head and body keys in a struct argument I give and send it back to the calling function. The response argument in the request function is a different struct type based on the calling function.
I'm unable to get this to work - I'm only getting back a blank struct from the request function. This is the fmt.PrintF log
client Response :
&{Body:{BOrderID:0 CC: Exch: EOID: Type: Local:0 Message: } Head:{ResponseCode: Status: StatusDescription:}}
This is an empty struct - ideally, it should be populated with the values retrieved from the API.
For reference, heres the struct passed as an argument as response in the request function:
type FPApiResponse struct {
Body FPBodyResponse `json:"body"`
Head FPHeadResponse `json:"head"`
}
type FPHeadResponse struct {
ResponseCode string `json:"responseCode"`
Status string `json:"status"`
StatusDescription string `json:"statusDescription"`
}
type FPBodyResponse struct {
BOrderID int `json:"BOrderID"`
CC string `json:"CC"`
Exch string `json:"Exch"`
EOID string `json:"EOID"`
Type string `json:"Type"`
Local int `json:"Local"`
Message string `json:"Message"`
}
Update
So I did this; instead of
responseData := FPApiSuccess{Head: response, Body: response}
I did this
responseData := fivePaisaApiSuccess{}
So now, I get the below in console
Client Response :
{Body:map[BOrderID:0 CC:52715111 Type:D Local:0 Message:Invalid Session ] Head:map[responseCode:5POrdReq status:0 statusDescription:Success]}
So essentially, this works, but the calling function doesn't seem to get the proper response:
Here's the calling function
func (fpc *FPClient) PlaceOrder(orderParams dto.OrderBodyParams, variety string, Token string) (string, *dto.AppError) {
var result FPApiResponse
headParams := dto.FFPOrderHeadParams{
//
}
FPOrderParams := dto.FPOrderParams{
//
}
FPErr := fpc.request(FPURL+FPPlaceOrder, http.MethodPost, FPOrderParams, brokerAccessToken, &result)
if FPErr != nil {
return "", FPErr
}
fmt.Printf("\n\n Client result : \n %+v \n", result)
if result.Head.Status != "0" {
//
}
if result.Body.Status != 0 {
//
}
return strconv.Itoa(result.Body.Broker), nil
}
The result value is blank:
{Body:{BOId:0 CC: Exch: Type: Local:0 Message: Status:0} Head:{ResponseCode: Status: StatusDescription:}}
I don't understand, this pointer is getting populated in the request function
Here's the struct I'm passing to the request:
type FPApiResponse struct {
Body FPBodyResponse `json:"body"`
Head FPHeadResponse `json:"head"`
}
i have parameter
id_user
phone_number
I want to decode to my struct
type User struct{
IDUser int `json:"id_user"`
PhoneNumber string `json:"phone_number"`
}
is it possible to decode into struct? I use gorilla schema. My Code:
func User(w http.ResponseWriter, r *http.Request){
var decoder = schema.NewDecoder()
var user User
if err := r.ParseForm(); err != nil {
fmt.Println(err)
}
err := decoder.Decode(&user, r.PostForm)
if err != nil {
fmt.Println(err)
}
respBody, err := json.Marshal(user)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(respBody)
}
i input id_user = 1 and phone_number = qwerty. But the result is id_user = 0 and phone_number = "".
If your mentioned two fields are query params you can directly read it like this way:
func User(w http.ResponseWriter, r *http.Request) {
idUser := r.URL.Query().Get("id_user")
phoneNumber := r.URL.Query().Get("phone_number")
var user User
id, err := strconv.Atoi(idUser)
if err != nil {
fmt.Println("error converting string to int")
return
}
user.IDUser = id
user.PhoneNumber = phoneNumber
respBody, _ := json.Marshal(user)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(respBody)
}
Otherwise you can directly pass the User struct in api payload and directly do the payLoad decoding like this
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
fmt.Println("error decoding api payload")
return
}
I think you should fix your tags from this:
type User struct{
IDUser int `json:id_user`
PhoneNumber string `json:phone_number`
}
to this:
type User struct{
IDUser int `json:"id_user"`
PhoneNumber string `json:"phone_number"`
}
So, you should use quotes in tag names.
If you want to decode them directly into a structure without getting the values of the fields one by one then you can use github.com/gorilla/schema. Refer to this question.
This answer may not be suitable for the question since the op was seeking help of using gorilla/schema package.
However, if someone were looking for a package to decode HTTP query params into a struct in Go, I will recommend an awesome package here:
ggicci/httpin
Disclaimer: I'm the creator and maintainer of this package.
httpin helps you easily decoding HTTP request data from
Query parameters, e.g. ?name=john&is_member=true
Headers, e.g. Authorization: xxx
Form data, e.g. username=john&password=******
JSON/XML Body, e.g. POST {"name":"john"}
Path variables, e.g. /users/{username}
File uploads
How to use?
type ListUsersInput struct {
Page int `in:"query=page"`
PerPage int `in:"query=per_page"`
IsMember bool `in:"query=is_member"`
}
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
if input.IsMember {
// Do sth.
}
// Do sth.
}
httpin is:
well documented: at https://ggicci.github.io/httpin/
well tested: coverage over 98%
open integrated: with net/http, go-chi/chi, gorilla/mux, gin-gonic/gin, etc.
extensible (advanced feature): by adding your custom directives. Read httpin - custom directives for more details.
awesome mentioned: https://github.com/avelino/awesome-go#forms