Unable to read "request.Body" in Go Chi router [duplicate] - go

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"`
}

Related

How to extract field from API response struct to another struct field while changing datatype?

I'm using range to loop through an array of structs to extract data which will be used as a URL parameter for my API calls. Within this loop, I'm trying to push response data from one struct to another.
I'm able to get everything working, except for moving data from one struct to another, but not entirely sure how to solve for the errors I keep getting. I've tried multiple methods and seem to be stuck in the mud here for something I don't consider to be too hard, until now... In my code I'm using the append method but I'm not so sure that might be the correct way to proceed.
Presenting my code:
models.go
//Here is my existing struct, with populated data that I get from a CSV
type TravelItenaries struct {
Origin string
Destination string
Flight_num string
Origin_latitude string
Origin_longitude string
Destination_latitude string
Destination_longitude string
Origin_weather string
Destination_weather string
Coordinates_ori string
Coordinates_dest string
Temp_c_ori string
Temp_f_ori string
Temp_c_dest string
Temp_f_dest string
}
//Here is the response data that I'm expected to get from my API calls.
//I'm trying to "push" Temp_c_dest and Temp_f_dest data into TravelItenaries.Temp_f_dest and TravelItenaries.Temp_c_dest
//While also changing the data types to fit above.
type Response struct {
Current struct {
LastUpdatedEpoch int `json:"last_updated_epoch"`
LastUpdated string `json:"last_updated"`
Temp_c_dest float64 `json:"temp_c"`
Temp_c_dest float64 `json:"temp_f"`
IsDay int `json:"is_day"`
} `json:"current"
}
weather.go
func (s *Server) getWeather(w http.ResponseWriter, r *http.Request) {
// open file
f, err := os.Open("challenge_dataset.csv")
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
// remember to close the file at the end of the program
defer f.Close()
// read csv values using csv.Reader
csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
// convert records to array of structs
travelItenaries := createTravelItenaries(data)
// remove duplicate flight records
cleanTravelItenaries:= remDupKeys(travelItenaries)
// set up params for API get request
params := url.Values{
"key": []string{"xxx"},
"q": []string{""},
}
// Construct URL for API request
u := &url.URL{
Scheme: "https",
Host: "api.weatherapi.com",
Path: "/v1/current.json",
RawQuery: params.Encode(),
}
client := &http.Client{}
// Will need this to populate the params using a range over a struct
values := u.Query()
// loop through cleaned data set
for _, service := range cleanTravelItenaries {
// dynamically acquire data from struct to pass as parameter
values.Set("q", service.Coordinates_dest)
u.RawQuery = values.Encode()
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
resp, err := client.Do(req)
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
body, err := ioutil.ReadAll(resp.Body)
// create empty struct to parse response data with using Inmarshal
var responseData models.Response
json.Unmarshal(body, &responseData)
// Here is the issue, I don't think append might be the correct procedure here?
// I simply just need to pass this response data to my already existing struct
service.Temp_c_dest = append(responseData.Current.Temp_c_dest , cleanTravelItenaries )
service.Temp_f_dest = append(responseData.Current.Temp_f_dest , cleanTravelItenaries )
}
}
The errors I get are related to both append statements at the end of the range function.
first argument to append must be slice; have float64
first argument to append must be slice; have float64
for both append methods.
Also, note how type TravelItenaries struct uses string type for:
Temp_c_dest string
Temp_f_dest string
Hence why I also need to do some field type conversion from Float64 to string.
How can I extract the fields Temp_c_dest and Temp_f_dest from API response struct to TravelItenaries struct fields while changing datatypes?
EDIT:
I've managed to get this somewhat working, but only inside the for loop. The data is not being saved outside the function.
service.Temp_f_dest = strconv.FormatFloat(responseData.Current.Temp_f_dest, 'g', -1, 64)
service.Temp_c_dest = strconv.FormatFloat(responseData.Current.Temp_c_dest, 'g', -1, 64)

Error while trying to fetch queryresult.KV object in JSON.Unmarshal

I am a little bit confused here and although I have searched a lot on this, something is clearly missing from my knowledge and I am asking your help.
I have created a Hyperledger Fabric Network and installed a chaincode in it. And I want to make a function that retrieves all the World State inputs about the Keys. I have done it already with the bytes.Buffer and it worked. But what I want to do is to do it with a struct.
So, I created the following struct that has only the key:
type WSKeys struct {
Key string `json: "key"`
Namespace string `json: "Namespace"`
}
And this is my code function:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArrayStr []WSKeys
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
var qry_key_json WSKeys
json.Unmarshal([]byte(queryResponse), &qry_key_json)
keyArray = append(keyArray, qry_key_json)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
When executing the above I get the following error:
cannot convert queryResponse (type *queryresult.KV) to type []byte
I can get the results correctly if I, for example do this:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArray []string
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
keyArray = append(keyArray, queryResponse.Key)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
But, why I get the above error when trying to add the queryResponse into a custom struct?
Do I need to add it to a struct that is only its type?
Please someone can explain what I am missing here?
The error statement is verbose enough to indicate, that your []byte conversion failed for the type queryResponse which, with a bit of lookup seems to be a struct type. In Go you cannot natively convert a struct instance to its constituent bytes without encoding using gob or other means.
Perhaps your intention was to use the Key record in the struct for un-marshalling
json.Unmarshal([]byte(queryResponse.Key), &qry_key_json)

Extra data in buffer error when decoding with gob - golang

I'm decoding with gob several objects fetched from a key/value database called "bitcask".
When I try to decode all of them one by one I get the "extra data in buffer" error from the gob decoder but exclusively for the first element that was added in the database and only after I've already fetched them at least once.
What am I doing wrong?
// Just the nest of all the bugs.
type Nest struct {
buf bytes.Buffer
dec *gob.Decoder
enc *gob.Encoder
db *bitcask.Bitcask
sync.Mutex
}
// Saves the bug into the nest.
func (n *Nest) Put(id int64, b Bug) error {
n.Lock()
defer n.Unlock()
if err := n.enc.Encode(b); err != nil {
return err
}
defer n.buf.Reset()
return n.db.Put(itosl(id), n.buf.Bytes())
}
// Retrieves a bug from the nest.
func (n *Nest) Get(id int64) (Bug, error) {
var bg Bug
b, err := n.db.Get(itosl(id))
if err != nil {
return bg, err
}
n.Lock()
defer n.Unlock()
if _, err = n.buf.Write(b); err != nil {
return bg, err
}
defer n.buf.Reset()
return bg, n.dec.Decode(&bg) // error extra data in buffer
}
Note: the "Get" function get's called for every ID in the database everytime the API endpoint is called.
The structs I'm encoding/decoding are the following:
type Comment struct {
Date int64 `json:"date"`
Text string `json:"text"`
Author string `json:"author"`
}
type Bug struct {
Id int64 `json:"id"`
Body string `json:"body"`
Open bool `json:"open"`
Tags []string `json:"tags"`
Date int64 `json:"date"`
Comments []Comment `json:"comments"`
Author string `json:"author"`
}
Also when I try to use a new decoder and buffer each time the "Get" function gets called (as I've seen in an answer to a similar question) the decode operation results in the following error: gob: unknown type id or corrupted data.
Refer to this link for the complete source code:
https://github.com/NicoNex/ladybug

POST call failing with JSON [duplicate]

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

How to decode query parameter in golang

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

Resources