Using PUT to update several JSON data in one request - json

I followed this guide Developing a simple CRUD API with Go, Gin and Gorm to build my first RESTful service in golang, I can read parameter(s) from my web service, and update one value of parameter, But how to parse the JSON to update several values of parameters at one PUT request.
The CURL command I intend to use for my test as bellow
$ curl -i -X PUT http://localhost:8080/params -d '{ [ {"id":"1","value": "10"}, {"id":"2","value": "20"}] }'
Following is my code
package main
import (
"fmt"
"log"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/go-sql-driver/mysql"
)
type Param struct {
ID int `json:"id" `
Name string `json:"name"`
Title string `json:"title"`
Value string `json:"value"`
}
type ParamValue struct {
ID int `json:"id" `
// Name string `json:"name"`
// Title string `json:"title"`
Value string `json:"value"`
}
var db *gorm.DB
var err error
func main() {
db, err = gorm.Open("mysql", "user:password#tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local")
defer db.Close()
if err != nil {
log.Panic(err.Error())
}
db.AutoMigrate(&Param{})
r := gin.Default()
r.GET("/params", GetParams)
r.GET("/params/:id", GetParam)
r.PUT("/params/:id", UpdateParam)
r.PUT("/params", UpdateParams) // How to implement this one
r.Use(cors.Default())
r.Run()
}
func GetParams(c *gin.Context) {
var params []Param
if err := db.Find(&params).Error; err != nil {
c.AbortWithStatus(404)
} else {
c.JSON(200, params)
}
}
func GetParam(c *gin.Context) {
id := c.Params.ByName("id")
var param Param
if err := db.Where("id = ?", id).First(&param).Error; err != nil {
c.AbortWithStatus(404)
} else {
c.JSON(200, param)
}
}
func UpdateParams(c *gin.Context) {
// Debug info
fmt.Println("c.Request.Method >> " + c.Request.Method)
fmt.Println("c.Request.URL.String() >> " + c.Request.URL.String())
// command for testing
// $ curl -i -X PUT http://localhost:8080/params -d '{ [ {"id":"1","value": "10"}, {"id":"2","value": "20"}] }'
}
func UpdateParam(c *gin.Context) {
var param Param
id := c.Params.ByName("id")
if err := db.Where("id = ?", id).First(&param).Error; err != nil {
c.AbortWithStatus(404)
} else {
name := param.Name // readonly
c.BindJSON(&param)
if param.Name != name { // if changed
param.Name = name // restore the origin
}
db.Save(&param)
c.JSON(200, param)
}
// $ curl -i -X PUT http://localhost:8080/params/63 -d '{ "name": "recharge", "title": "switch 0/1:on/off", "value": "1"}'
}
Any help is appreciated.

first of all, you json is invalid.
I guess that this is what you wanted:
[{"id":"1" ,"value": "10"}, {"id":"2" ,"value": "20"}]
an easier solution will be to add an struct that match your json:
and then add the json.Unmarshal to parse the json:
jsn := `[{"id":"1" ,"value": "10"}, {"id":"2" ,"value": "20"}]`
var data yourSturctName
json.Unmarshal([]byte(jsn), &data)
for _, r := range data {
fmt.Printf("ID:%s Value:%s", r.ID, r.Value)
}
then add that into your logic.

Related

Golang how to pass username and password as JSON body to Post API

Using a post API request I want to receive a JWT token for the response.
For that, I want to send the username and password as JSON body input to the API. The username and password should be taken dynamically from the user as a JSON payload.
Currently, I am hardcoding the user name and password using strings.NewReader which is not suitable as this is a web application and many users will be using it.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/joho/godotenv"
)
var authToken string
type authorization struct {
AuthorizationToken string `json:"authorizationToken"`
ExpiresTimestamp string `json:"expiresTimestamp"`
}
func main() {
enverr := godotenv.Load(".env")
if enverr != nil {
fmt.Println("Error loading .env file")
os.Exit(1)
}
token()
}
func token() {
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.NewReader(`
{
"name" : "testuser",
"pwd" : "testpwd",
"hName" : "hname"
}
`)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
if err != nil {
panic(err)
}
defer response.Body.Close()
content, _ := ioutil.ReadAll(response.Body)
var result authorization
if err := json.Unmarshal(content, &result); err != nil { // Parse []byte to the go struct pointer
fmt.Println("Can not unmarshal JSON")
}
authToken := result.AuthorizationToken
fmt.Println(PrettyPrint(authToken))
}
func PrettyPrint(i interface{}) string {
s, _ := json.MarshalIndent(i, "", "\t")
return string(s)
}
You can use http requests body (for POST request)
type Credentials struct {
Username string `json:"username"`
Password string `json:"password"`
}
c := Credentials{}
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
err: = decoder.Decode(&c)
if err!=nil{
// handle it
}
// use it
fmt.Println(c.Username, c.Password)
If you are intending to distribute the application as an executable (aka command line in the Unix world), you can either:
Provide the username and password as program arguments, os.Args (omitting unchanged code):
var inputTemplate = `
{
"name" : "USER",
"pwd" : "PWD",
"hName" : "hname"
}
`
func token() {
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.Replace(strings.Replace(inputTemplate, "PWD", os.Args[2], -1), "USER", os.Args[1], -1)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
// ...
}
You should then be able to run your program (once compiled): ./filename user password
Or as better alternative, having sensitive data involved, have the username and password input from standard input with password being echoed (hiding the characters):
import (
// ...
"syscall"
"golang.org/x/term"
)
var inputTemplate = `
{
"name" : "USER",
"pwd" : "PWD",
"hName" : "hname"
}
`
func token() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter username: ")
username := reader.ReadString('\n') // handle error
fmt.Print("Enter password: ")
pwdbytes := term.ReadPassword(int(syscall.Stdin))
password := string(pwdbytes)
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.Replace(strings.Replace(inputTemplate, "PWD", password, -1), "USER", username, -1)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
// ...
}

Golang Batch Post Via Mux

Hello I'm new to Golang and I'm attempting to do a batch POST with Mux. I want to be able to POST multiple "produce" items instead of just a single one.
Here I'm defining what a produce item is
// Define the produce structure
type Produce struct {
Name string `json:"name"`
Code string `json:"code"`
Unit_Price float64 `json:"unit_price"`
}
// Init produce var as a Produce slice
var produce []Produce
Here is my current POST code
func addProduce(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var newProduceItem Produce
_ = json.NewDecoder(r.Body).Decode(&newProduceItem)
re := regexp.MustCompile("^[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}$")
if re.MatchString(newProduceItem.Code) == true && len(newProduceItem.Name) > 0 {
newProduceItem.Unit_Price = math.Round(newProduceItem.Unit_Price*100) / 100 //rounds to the nearest cent
produce = append(produce, newProduceItem)
json.NewEncoder(w).Encode(newProduceItem)
} else {
http.Error(w, fmt.Sprintf("Incorrect produce code sequence or product name. Example code sequence: A12T-4GH7-QPL9-3N4M"), http.StatusBadRequest)
}
}
It is called in the main() function as seen here.
func main() {
router := mux.NewRouter()
router.HandleFunc("/produce", addProduce).Methods("POST")
log.Fatal(http.ListenAndServe(":8000", router))
}
Here an example of JSON data that is working when I POST to it in Postman
{
"name":"Peach",
"code": "TTTT-44D4-A12T-1224",
"unit_price": 5.3334
}
I want to be able to post multiple produce items at once such as....
[
{
"name": "Green Pepper",
"code": "YRT6-72AS-K736-L4AR",
"unit_price": 0.79
},
{
"name": "Gala Apple",
"code": "TQ4C-VV6T-75ZX-1RMR",
"unit_price": 3.59
},
]
Thank you
There are clearly many ways to go about it, here is one
package main
import (
"encoding/json"
"fmt"
"log"
"math"
"net/http"
"regexp"
"github.com/gorilla/mux"
)
type Produce struct {
Name string `json:"name"`
Code string `json:"code"`
Unit_Price float64 `json:"unit_price"`
}
type ProduceList []Produce
// global var where all produce is kept,
// not persistent
var produce ProduceList
func addProduce(w http.ResponseWriter, r *http.Request) {
// we accept a json and decode it into a slice of structs
var newProduceItems ProduceList
err := json.NewDecoder(r.Body).Decode(&newProduceItems)
if err != nil {
log.Panic(err)
}
var tempItems ProduceList
re := regexp.MustCompile("^[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}$")
// iterate over each element in the posted json and validate
// when validated, add to the temporary accumulator
// if not validated, error out and stop
for idx, produceItem := range newProduceItems {
if !re.MatchString(produceItem.Code) || len(produceItem.Name) <= 0 {
errMsg := fmt.Sprintf("Item %d: Incorrect produce code sequence or product name. Example code sequence: A12T-4GH7-QPL9-3N4M", idx)
http.Error(w, errMsg, http.StatusBadRequest)
return
}
produceItem.Unit_Price = math.Round(produceItem.Unit_Price*100) / 100 //rounds to the nearest cent
tempItems = append(tempItems, produceItem)
}
// after validation, append new items to the global accumulator and respond back with added items
produce = append(produce, tempItems...)
w.Header().Set("Content-Type", "application/json")
if err = json.NewEncoder(w).Encode(newProduceItems); err != nil {
log.Panic(err)
}
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/produce", addProduce).Methods("POST")
log.Fatal(http.ListenAndServe(":8000", router))
}

Add a field to JSON ( struct + interface ) golang [duplicate]

This question already has answers here:
Adding Arbitrary fields to json output of an unknown struct
(2 answers)
Closed 1 year ago.
Here's the response interface :
type Response interface{}
It's satisfied by a struct like this :
type CheckResponse struct {
Status string `json:"status"`
}
I am getting out []Response as an output which is to be consumed elsewhere.
I want to add a Version string to this JSON, before it's being sent. I've tried using anonymous structs ( but in vain ) :
for _, d := range out {
outd := struct {
Resp Response `json:",inline"`
Version string `json:",inline"`
}{
Resp: d,
Version: "1.1",
}
data, _ := json.Marshal(outd)
log.Infof("response : %s", data)
}
The output I am getting is :
response : {"Resp":{"status":"UP"},"Version":"1.1"}
What I want is
{"status":"UP","Version":"1.1"}
i.e. one single flat JSON.
Assert your d to CheckResponse type and then define dynamic struct like this
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}
This is the full code for this.
package main
import (
"encoding/json"
"fmt"
)
type Response interface {}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
out := []Response{
CheckResponse{Status: "UP"},
}
for _, d := range out {
res, ok := d.(CheckResponse)
if !ok {
continue
}
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}{
Resp: res.Status,
Version: "1.1",
}
data, _ := json.Marshal(outd)
fmt.Printf("response : %s", data)
}
}
You can run here
inline tag is not supported by encoding/json and embedding interfaces will also not produce the result you want. You'll have to declare a type for the out value and have that type implement the json.Marshaler interface, you can then customize how its fields are marshaled, for example you could marshal the two fields Resp and Version separately and then "merge the result" into a single json object.
type VersionedResponse struct {
Resp Response
Version string
}
func (r VersionedResponse) MarshalJSON() ([]byte, error) {
out1, err := json.Marshal(r.Resp)
if err != nil {
return nil, err
}
out2, err := json.Marshal(struct{ Version string }{r.Version})
if err != nil {
return nil, err
}
// NOTE: if Resp can hold something that after marshaling
// produces something other than a json object, you'll have
// to be more careful about how you gonna merge the two outputs.
//
// For example if Resp is nil then out1 will be []byte(`null`)
// If Resp is a slice then out1 will be []byte(`[ ... ]`)
out1[len(out1)-1] = ',' // replace '}' with ','
out2 = out2[1:] // remove leading '{'
return append(out1, out2...), nil
}
https://play.golang.org/p/66jIYXGUtWJ
One way that will work for sure is simply use a map[string]interface{}, iterate over fields in Response via reflect or use a library like structs, update your map with response fields, append your version field to map, and then marshal.
Here is an example
package main
import (
"encoding/json"
"fmt"
"github.com/fatih/structs"
)
type Response interface{}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
resp := CheckResponse{Status: "success"}
m := structs.Map(resp)
m["Version"] = "0.1"
out, _ := json.Marshal(m)
fmt.Println(string(out))
}

Getting a Bad response - 422 from the server while sending a POST request in GO

I am trying to send a POST request using this function -
{
func (Client *Client) doModify(method string, url string, createObj interface{}, respObject interface{}) error {
bodyContent, err := json.Marshal(createObj)
if err != nil {
return err
}
client := Client.newHttpClient()
req, err := http.NewRequest(method, url, bytes.NewBuffer(bodyContent))
if err != nil {
return err
}
Client.setupRequest(req)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Length", string(len(bodyContent)))
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return errors.New(fmt.Sprintf("Bad response from [%s], go [%d]", url, resp.StatusCode))
}
byteContent, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return json.Unmarshal(byteContent, respObject)
}
}
I am calling my function like this -
{
func TestContainerCreate(t *testing.T) {
client := newClient(t)
container, err := client.Container.Create(&Container{
Name: "name",
ImageUuid: "xyz",
})
if err != nil {
t.Fatal(err)
}
defer client.Container.Delete(container)
}
}
The Create function calls internally calls the doCreate function which calls the doModify function pasted on the top .
{
func (self *ContainerClient) Create(container *Container) (*Container, error) {
resp := &Container{}
err := self.Client.doCreate(container_TYPE, container, resp)
return resp, err
}
}
{
func (Client *Client) doCreate(schemaType string, createObj interface{}, respObject interface{}) error {
if createObj == nil {
createObj = map[string]string{}
}
schema, ok := Client.Types[schemaType]
if !ok {
return errors.New("Unknown schema type [" + schemaType + "]")
}
return Client.doModify("POST", collectionUrl, createObj, respObject)
}
}
This gives me a 422 bad response.On doing further research, When doing a CURL, with "name" and "imageUuid" first letter as small case, gives a 201 created status but when passing "Name" and "ImageUuid" first letter as capital gives 422 bad response. Could there be issue with the json struct defined for container, or case of these entities being defined or something else?
{
curl -X POST -v -s http://localhost:8080/v1/containers -H 'Content-Type: application/json' -d '{"name" : "demo", "imageUuid" : "docker:nginx"}' | python -m 'json.tool'
}
Container struct definition looks like this -
{
type Container struct {
Resource
ImageId string `json:"ImageId,omitempty"`
ImageUuid string `json:"ImageUuid,omitempty"`
MemoryMb int `json:"MemoryMb,omitempty"`
Name string `json:"Name,omitempty"`
}
type ContainerCollection struct {
Collection
Data []Container `json:"data,omitempty"`
}
}
string(len(bodyContent)) isn't doing what you think it is. You're converting a single int to a utf-8 string. You want to use the strconv package to get the numerical representation.
Also note that you can't omitempty an int, since 0 is a valid value.

Handling JSON Post Request in Go

So I have the following, which seems incredibly hacky, and I've been thinking to myself that Go has better designed libraries than this, but I can't find an example of Go handling a POST request of JSON data. They are all form POSTs.
Here is an example request: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test
And here is the code, with the logs embedded:
package main
import (
"encoding/json"
"log"
"net/http"
)
type test_struct struct {
Test string
}
func test(rw http.ResponseWriter, req *http.Request) {
req.ParseForm()
log.Println(req.Form)
//LOG: map[{"test": "that"}:[]]
var t test_struct
for key, _ := range req.Form {
log.Println(key)
//LOG: {"test": "that"}
err := json.Unmarshal([]byte(key), &t)
if err != nil {
log.Println(err.Error())
}
}
log.Println(t.Test)
//LOG: that
}
func main() {
http.HandleFunc("/test", test)
log.Fatal(http.ListenAndServe(":8082", nil))
}
There's got to be a better way, right? I'm just stumped in finding what the best practice could be.
(Go is also known as Golang to the search engines, and mentioned here so others can find it.)
Please use json.Decoder instead of json.Unmarshal.
func test(rw http.ResponseWriter, req *http.Request) {
decoder := json.NewDecoder(req.Body)
var t test_struct
err := decoder.Decode(&t)
if err != nil {
panic(err)
}
log.Println(t.Test)
}
You need to read from req.Body. The ParseForm method is reading from the req.Body and then parsing it in standard HTTP encoded format. What you want is to read the body and parse it in JSON format.
Here's your code updated.
package main
import (
"encoding/json"
"log"
"net/http"
"io/ioutil"
)
type test_struct struct {
Test string
}
func test(rw http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
panic(err)
}
log.Println(string(body))
var t test_struct
err = json.Unmarshal(body, &t)
if err != nil {
panic(err)
}
log.Println(t.Test)
}
func main() {
http.HandleFunc("/test", test)
log.Fatal(http.ListenAndServe(":8082", nil))
}
There are two reasons why json.Decoder should be preferred over json.Unmarshal - that are not addressed in the most popular answer from 2013:
February 2018, go 1.10 introduced a new method json.Decoder.DisallowUnknownFields() which addresses the concern of detecting unwanted JSON-input
req.Body is already an io.Reader. Reading its entire contents and then performing json.Unmarshal wastes resources if the stream was, say a 10MB block of invalid JSON. Parsing the request body, with json.Decoder, as it streams in would trigger an early parse error if invalid JSON was encountered. Processing I/O streams in realtime is the preferred go-way.
Addressing some of the user comments about detecting bad user input:
To enforce mandatory fields, and other sanitation checks, try:
d := json.NewDecoder(req.Body)
d.DisallowUnknownFields() // catch unwanted fields
// anonymous struct type: handy for one-time use
t := struct {
Test *string `json:"test"` // pointer so we can test for field absence
}{}
err := d.Decode(&t)
if err != nil {
// bad JSON or unrecognized json field
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
if t.Test == nil {
http.Error(rw, "missing field 'test' from JSON object", http.StatusBadRequest)
return
}
// optional extra check
if d.More() {
http.Error(rw, "extraneous data after JSON object", http.StatusBadRequest)
return
}
// got the input we expected: no more, no less
log.Println(*t.Test)
Playground
Typical output:
$ curl -X POST -d "{}" http://localhost:8082/strict_test
expected json field 'test'
$ curl -X POST -d "{\"Test\":\"maybe?\",\"Unwanted\":\"1\"}" http://localhost:8082/strict_test
json: unknown field "Unwanted"
$ curl -X POST -d "{\"Test\":\"oops\"}g4rB4g3##$%^&*" http://localhost:8082/strict_test
extraneous data after JSON
$ curl -X POST -d "{\"Test\":\"Works\"}" http://localhost:8082/strict_test
log: 2019/03/07 16:03:13 Works
I was driving myself crazy with this exact problem. My JSON Marshaller and Unmarshaller were not populating my Go struct. Then I found the solution at https://eager.io/blog/go-and-json:
"As with all structs in Go, it’s important to remember that only
fields with a capital first letter are visible to external programs
like the JSON Marshaller."
After that, my Marshaller and Unmarshaller worked perfectly!
I found the following example from the docs really helpful (source here).
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
func main() {
const jsonStream = `
{"Name": "Ed", "Text": "Knock knock."}
{"Name": "Sam", "Text": "Who's there?"}
{"Name": "Ed", "Text": "Go fmt."}
{"Name": "Sam", "Text": "Go fmt who?"}
{"Name": "Ed", "Text": "Go fmt yourself!"}
`
type Message struct {
Name, Text string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var m Message
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", m.Name, m.Text)
}
}
The key here being that the OP was looking to decode
type test_struct struct {
Test string
}
...in which case we would drop the const jsonStream, and replace the Message struct with the test_struct:
func test(rw http.ResponseWriter, req *http.Request) {
dec := json.NewDecoder(req.Body)
for {
var t test_struct
if err := dec.Decode(&t); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
log.Printf("%s\n", t.Test)
}
}
Update: I would also add that this post provides some great data about responding with JSON as well. The author explains struct tags, which I was not aware of.
Since JSON does not normally look like {"Test": "test", "SomeKey": "SomeVal"}, but rather {"test": "test", "somekey": "some value"}, you can restructure your struct like this:
type test_struct struct {
Test string `json:"test"`
SomeKey string `json:"some-key"`
}
...and now your handler will parse JSON using "some-key" as opposed to "SomeKey" (which you will be using internally).
I like to define custom structs locally. So:
// my handler func
func addImage(w http.ResponseWriter, r *http.Request) {
// define custom type
type Input struct {
Url string `json:"url"`
Name string `json:"name"`
Priority int8 `json:"priority"`
}
// define a var
var input Input
// decode input or return error
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
w.WriteHeader(400)
fmt.Fprintf(w, "Decode error! please check your JSON formating.")
return
}
// print user inputs
fmt.Fprintf(w, "Inputed name: %s", input.Name)
}
type test struct {
Test string `json:"test"`
}
func test(w http.ResponseWriter, req *http.Request) {
var t test_struct
body, _ := ioutil.ReadAll(req.Body)
json.Unmarshal(body, &t)
fmt.Println(t)
}