Chi empty http.Request.Body in render.Bind - json

I am using github.com/pressly/chi to build this simple program where I try to decode some JSON from the http.Request.Body:
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/pressly/chi"
"github.com/pressly/chi/render"
)
type Test struct {
Name string `json:"name"`
}
func (p *Test) Bind(r *http.Request) error {
err := json.NewDecoder(r.Body).Decode(p)
if err != nil {
return err
}
return nil
}
func main() {
r := chi.NewRouter()
r.Post("/products", func(w http.ResponseWriter, r *http.Request) {
var p Test
// err := render.Bind(r, &p)
err := json.NewDecoder(r.Body).Decode(&p)
if err != nil {
panic(err)
}
fmt.Println(p)
})
http.ListenAndServe(":8080", r)
}
When I don't use render.Bind() (from "github.com/pressly/chi/render"), it works as expected.
However, when I uncomment the line err := render.Bind(r, &p) and I comment the line err := json.NewDecoder(r.Body).Decode(&p), it panics with EOF :
2017/06/20 22:26:39 http: panic serving 127.0.0.1:39696: EOF
and thus the json.Decode() fails.
Am I doing something wrong or is the http.Request.Body is already read somewhere else before render.Bind() is called?

render.Bind's purpose is to perform decode and execute Bind(r) to do post decode operations.
For eg.:
type Test struct {
Name string `json:"name"`
}
func (p *Test) Bind(r *http.Request) error {
// At this point, Decode is already done by `chi`
p.Name = p.Name + " after decode"
return nil
}
If you have to do only JSON decode no other actions needs to be done after decode with respect to decoded values. Just use:
// Use Directly JSON decoder of std pkg
err := json.NewDecoder(r.Body).Decode(&p)
OR
// Use wrapper method from chi DecodeJSON
err := render.DecodeJSON(r.Body, &p)

Related

Golang json marshal and encoding give weird output

I am trying to customise error message for my db query . Following is what I am doing first I create struct Errormessage . Next if there is error in db.query I do this marshaling then encoding and return. But I end up getting this output "e30=" on my postman testing. What could be wrong I check and followed few examples are showing this mechanism ?
error1 := Errormessage{"Error in select"}
error1_enc,errEn := json.Marshal(error1)
if errEn != nil {
// if error is not nil
// print error
fmt.Println(errEn)
}
json.NewEncoder(w).Encode(error1_enc)
return
/
/ declaring a struct
type Errormessage struct{
// defining struct variables
errormessage string
}
func checkExistUser(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println("File Name :", r.FormValue("email"))
result, err := db.Query("SELECT * from userDetailsss")
if err != nil {
//http.Error(w, err, 500)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(400)
fmt.Println(err)
error1 := Errormessage{"Error in select"}
error1_enc,errEn := json.Marshal(error1)
if errEn != nil {
// if error is not nil
// print error
fmt.Println(errEn)
}
json.NewEncoder(w).Encode(error1_enc)
return
//panic(err.Error())
}
// This part is how my db is defined and opened
var db *sql.DB
var err error
func main() {
db, err = sql.Open("mysql", "******##tcp(127.0.0.1:3306)/****")
if err != nil {
panic(err.Error())
}
defer db.Close()
router := mux.NewRouter()
router.HandleFunc("/", DoHealthCheck).Methods("POST")
router.HandleFunc("/checkExistUser", checkExistUser).Methods("POST")
log.Fatal(http.ListenAndServe(":8080", router))
}
There are two issues with your code:
You are json encoding the already json encoded error. This means that you are json encoding raw json bytes, which is the reason for the weird output.
Your Errormessage struct's field is unexported. Unexported fields will not be encoded by the encoding/json package.
To fix #1 you can do:
func checkExistUser(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println("File Name :", r.FormValue("email"))
result, err := db.Query("SELECT * from userDetailsss")
if err != nil {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(400)
// use only Encode, no need to call json.Marshal
if err := json.NewEncoder(w).Encode(Errormessage{"Error in select"}); err != nil {
log.Println("failed to send reposnse:", err)
}
return
}
// ...
}
To fix #2 you can do:
type Errormessage struct {
// export the field, i.e. change it to start with an upper case letter
Errormessage string `json:"errormessage"`
}

Golang Read JSON from S3 into struct in memory

I have a JSON file in S3 that takes the format of the following struct:
type StockInfo []struct {
Ticker string `json:"ticker"`
BoughtPrice string `json:"boughtPrice"`
NumberOfShares string `json:"numberOfShares"`
}
and I want to read it into a struct value from S3. In python the code would look something like this:
import boto3
import json
s3 = boto3.client('s3', 'us-east-1')
obj = s3.get_object(Bucket=os.environ["BucketName"], Key=os.environ["Key"])
fileContents = obj['Body'].read().decode('utf-8')
json_content = json.loads(fileContents)
However I'm kinda stuck on how to make this happen in Go. I've gotten this far:
package main
import (
"archive/tar"
"bytes"
"fmt"
"log"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/joho/godotenv"
)
type StockInfo []struct {
Ticker string `json:"ticker"`
BoughtPrice string `json:"boughtPrice"`
NumberOfShares string `json:"numberOfShares"`
}
func init() {
// loads values from .env into the system
if err := godotenv.Load(); err != nil {
log.Print("No .env file found")
}
return
}
func main() {
// Store the PATH environment variable in a variable
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1")},
)
if err != nil {
panic(err)
}
s3Client := s3.New(sess)
bucket := "ian-test-bucket-go-python"
key := "StockInfo.json"
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
result, err := s3Client.GetObject(requestInput)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
which returns to me the body/object buffer, but im not sure how to read that into a string so I can marshal it into my struct. I found this code in a similar question:
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
buf := new(aws.WriteAtBuffer)
numBytes, _ := *s3manager.Downloader.Download(buf, requestInput)
tr := tar.NewReader(bytes.NewReader(buf.Bytes()))
but I get the following errors:
not enough arguments in call to method expression s3manager.Downloader.Download
have (*aws.WriteAtBuffer, *s3.GetObjectInput)
want (s3manager.Downloader, io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader))
multiple-value s3manager.Downloader.Download() in single-value context
Can anyone point me in the right direction? kinda frustrating how hard it seems to do this compared to python.
I was able to do it with the following code:
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
result, err := s3Client.GetObject(requestInput)
if err != nil {
fmt.Println(err)
}
defer result.Body.Close()
body1, err := ioutil.ReadAll(result.Body)
if err != nil {
fmt.Println(err)
}
bodyString1 := fmt.Sprintf("%s", body1)
var s3data StockInfo
decoder := json.NewDecoder(strings.NewReader(bodyString1))
err = decoder.Decode(&s3data)
if err != nil {
fmt.Println("twas an error")
}
fmt.Println(s3data)
Alternative solution using json.Unmarshal
besed on aws-sdk-go-v2
...
params := &s3.GetObjectInput{
Bucket: aws.String(s3Record.S3.Bucket.Name),
Key: aws.String(s3Record.S3.Object.Key),
}
result, _ := client.GetObject(context.TODO(), params)
if err != nil {
panic(err)
}
defer result.Body.Close()
// capture all bytes from upload
b, err := ioutil.ReadAll(result.Body)
if err != nil {
panic(err)
}
var temp StockInfo
if err = json.Unmarshal(b, &temp); err != nil {
panic(err)
}
ftm.Println("res: ",b)

Print/Log full unstructured json from stream in Go

I inherited someone else's code for an API and since I'm not familiar with the requests that it's receiving I'm trying to print them or log them so I can see their structure. From what I've read about Go, jsons are decoded with Structs but since I don't know how the requests are received I cant write a struct.
I've tried the following on a basic API but they just print me out an empty map or nothing at all:
func createBook(w http.ResponseWriter, r *http.Request) {
var result map[string]interface{}
_ = json.NewDecoder(r.Body).Decode(&result)
fmt.Println(result)
func createBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var book Book
_ = json.NewDecoder(r.Body).Decode(&book)
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
newStr := buf.String()
reader := strings.NewReader(newStr)
writter := os.Stdout
dec := json.NewDecoder(reader)
enc := json.NewEncoder(writter)
for {
var m map[string]interface{}
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
if err := enc.Encode(&m); err != nil {
log.Println(err)
}
fmt.Println(m)
}
book.ID = strconv.Itoa(rand.Intn(100000000)) // Mock ID - not safe
books = append(books, book)
json.NewEncoder(w).Encode(book)
}
Is there any other way that it would print the received json without me knowing the strut beforehand?
Use json.Unmarshal function
import "bytes"
func createBook(w http.ResponseWriter, r *http.Request) {
var result map[string]interface{}
data :+ StreamToByte(r.Body)
err := json.Unmarshal(data, &result)
if err !=nil{
fmt.Println(err) //better to use log
}else
fmt.Println(result)
}
}
func StreamToByte(stream io.Reader) []byte {
buf := new(bytes.Buffer)
buf.ReadFrom(stream)
return buf.Bytes()
}
Refer :
https://appdividend.com/2020/02/28/golang-how-to-convert-json-to-map-in-go/
https://gist.github.com/dixudx/3989284b142414e10352fde9def5c771

Printing decoded JSON in Golang

I am very new to Go / programming in general - having just picked it up whilst messing about creating my own crypto currency portfolio web site.
I am struggling printing to the web server output. If I used Printf - it prints to console but as soon as I use Fprintf to print to the web app, I get a number of errors which I can't seem to solve.
Could someone walk me through it?
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Obsidian []struct {
PriceUsd string `json:"price_usd"`
PriceBtc string `json:"price_btc"`
}
func webserver(w http.ResponseWriter, r *http.Request) {
url := "https://api.coinmarketcap.com/v1/ticker/obsidian/"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal("NewRequest: ", err)
return
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal("Do: ", err)
return
}
defer resp.Body.Close()
var record Obsidian
if err := json.NewDecoder(resp.Body).Decode(&record); err != nil {
log.Println(err)
}
fmt.Printf("%+v", record)
}
func main() {
http.HandleFunc("/test", webserver)
http.ListenAndServe(":8001", nil)
}
I have tried to replace:
fmt.Printf("%+v", record)
with:
fmt.Fprintf("%+v", record)
and receive the following errors:
./test.go:54:21: cannot use "%+v" (type string) as type io.Writer in argument to fmt.Fprintf:
string does not implement io.Writer (missing Write method)
./test.go:54:21: cannot use record (type Obsidian) as type string in argument to fmt.Fprintf
Thanks to #MiloChrisstiansen
fmt.Fprintf(w, "%+v", record)
You could also use
w.Write([]byte(record))

Golang Converting JSON

map[key:2073933158088]
I need to grab the key out of this data structure as a string, but I can't seem to figure out how!
Help with this overly simple question very much appreciated.
The value above is encapsulated in the variable named data.
I have tried: data.key, data[key], data["key"], data[0] and none of these seem to be appropriate calls.
To define data I sent up a JSON packet to a queue on IronMQ. I then pulled the message from the queue and manipulated it like this:
payloadIndex := 0
for index, arg := range(os.Args) {
if arg == "-payload" {
payloadIndex = index + 1
}
}
if payloadIndex >= len(os.Args) {
panic("No payload value.")
}
payload := os.Args[payloadIndex]
var data interface{}
raw, err := ioutil.ReadFile(payload)
if err != nil {
panic(err.Error())
}
err = json.Unmarshal(raw, &data)
Design your data type to match json structure. This is how can you achieve this:
package main
import (
"fmt"
"encoding/json"
)
type Data struct {
Key string `json:"key"`
}
func main() {
data := new(Data)
text := `{ "key": "2073933158088" }`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
Since the number in the json is unquoted, it's not a string, Go will panic if you try to just handle it as a string (playground: http://play.golang.org/p/i-NUwchJc1).
Here's a working alternative:
package main
import (
"fmt"
"encoding/json"
"strconv"
)
type Data struct {
Key string `json:"key"`
}
func (d *Data) UnmarshalJSON(content []byte) error {
var m map[string]interface{}
err := json.Unmarshal(content, &m)
if err != nil {
return err
}
d.Key = strconv.FormatFloat(m["key"].(float64), 'f', -1, 64)
return nil
}
func main() {
data := new(Data)
text := `{"key":2073933158088}`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
You can see the result in the playground: http://play.golang.org/p/5hU3hdV3kK