I have an issue printing the JSON from the POST.
I use gorilla/mux for routing
r := mux.NewRouter()
r.HandleFunc("/test", Point).Methods("POST")
http.ListenAndServe(":80", r)`
and in Point function I have
func Point(w http.ResponseWriter, r *http.Request) {
var callback Decoder
json.NewDecoder(r.Body).Decode(&callback)
}
But I can use this method only when I know the structure and I want to figure out how to log.Print the whole JSON as a string. I tried
func Point(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
log.Println(r.Form)
}
But it prints an empty map. Please help to figure out this.
In the assumption that you are building a standard API endpoint which receives some JSON and you would like to do something with it you should approach it the following way.
Edit:
As mentioned in the comments when you use the ioutil.ReadAll()
function as in this example it will read everything that is sent in the
post request into the application memory. It's a good idea to check
this in a production application (e.g limiting payload size).
1.) Make a struct which will hold the data coming from an API post request in GoLang
2.) Convert your request body into a byte array []byte
3.) Unmarshal your []byte into a single instance of your struct made earlier.
I'll put an example below:
1. Make a struct which you'll put your JSON into.
Let's take an example of a simple blog post.
The JSON object looks like this and has a slug, a title, and description
{
"slug": "test-slug",
"title": "This is the title",
"body": "This is the body of the page"
}
So your struct would look like this:
type Page struct {
Slug string `json:"slug"`
Title string `json:"title"`
Body string `json:"body"`
}
2 - 3. Get the body of your request and convert it to byte[] Then take that string and Unmarshal it into an instance of your struct.
The data of a post request is the request 'Body'.
In Golang the request in almost all cases (unless you're using something fancy outside of the defaults) will be an http.Request object. This is the 'r' which you normally have in your normal code and it holds the 'Body' of our POST request.
import (
"encoding/json"
"github.com/go-chi/chi" // you can remove
"github.com/go-chi/render" // you can remove but be sure to remove where it is used as well below.
"io/ioutil"
"net/http"
)
func GiveMeAPage(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("A page"))
}
So what we are going to do here is convert an io.ReadCloser, which is what the http.Request.Body is, to a []byte as the Unmarshal function takes a []byte type. I've commented inline below for you.
func Create(w http.ResponseWriter, r *http.Request) {
var p Page //Create an instance of our struct
//Read all the data in r.Body from a byte[], convert it to a string, and assign store it in 's'.
s, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err) // This would normally be a normal Error http response but I've put this here so it's easy for you to test.
}
// use the built in Unmarshal function to put the string we got above into the empty page we created at the top. Notice the &p. The & is important, if you don't understand it go and do the 'Tour of Go' again.
err = json.Unmarshal(s, &p)
if err != nil {
panic(err) // This would normally be a normal Error http response but I've put this here so it's easy for you to test.
}
// From here you have a proper Page object which is filled. Do what you want with it.
render.JSON(w, r, p) // This is me using a useful helper function from go-chi which 'Marshals' a struct to a json string and returns it to using the http.ResponseWriter.
}
As a side note. Please don't use Decoder to parse JSON unless you are using JSON streams. You are not here, and it's unlikely you will
for a while. You can read about why that is
here
If you just want the raw JSON data without parsing it, http.Request.Body implements io.Reader, so you can just Read from it. For example with ioutil.ReadAll.
Something like (untested):
func Point(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
// check error
// do whatever you want with the raw data (you can `json.Unmarshal` it too)
}
Related
Using Go, I'm attempting to fetch a few JSON responses concurrently from multiple endpoints. I'd like to attach each of these responses to fields in a struct or map and return this struct/map as a JSON object. (Backend for Frontend pattern). So I will make a web request to the Go application with some sort of identifier. It will in turn make several web requests, and compile the data into one large object to return as a response.
I'm using Fiber as my framework but any generic web framework would be similar:
app.Get("/requests/:identifier", func(c *fiber.Ctx) error {
identifier := c.Params("identifier")
timeout := 1600 * time.Millisecond
client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))
res, err := client.Get("https://www.example.com/endpoint?id=" + identifier, nil)
if err != nil{
logger.Error("Timout value exceeded")
return c.Status(503).SendString("Socket Timeout")
}
logger.Info("Fetch success: ")
// Heimdall returns the standard *http.Response object
body, err := ioutil.ReadAll(res.Body)
code := 200
response := &main_response{
Service1: body,
}
return c.Status(code).JSON(response)
})
The problem I'm having is, I have no need to unmarshal this data in Go, as I have no use for it (I am simply passing it along). Do I have to unmarshal it just so I can set it as a field in my response struct like this?
type main_response struct {
Service1 []byte `json:"service1"`
Service2 map[string]string `json:"service2"`
Service3 map[string]interface{} `json:"service3"`
}
(I've tried a few different ways to accomplish this. Trying to use a byte array seems to base64 encode the responses)
I will want to marshal that struct to JSON before returning it, so perhaps I have little choice as I can't think of a way to tell Go "only marshal the main struct, everything else is already JSON". It almost feels like I'd be better off building a string at this point.
Use json.RawMessage to copy a []byte containing JSON directly to the response JSON document:
type main_response struct {
Service1 json.RawMessage `json:"service1"`
...
}
response := &main_response{
Service1: body,
}
return c.Status(code).JSON(response)
I would like to take an arbitrary http.Request and get the body as a json string. I know this involves the json package, but it seems json.Decode needs a specific struct passed in by reference. How can I decode an arbitrary request body (and then stringify the result)?
func RequestBodyJsonString(r *http.Request) string {
}
Use ioutil.ReadAll to get data in byte slice then type conversion to string to get json string
bytedata, err := ioutil.ReadAll(r.Body)
reqBodyString := string(data)
An example in go playground here
This question already has an answer here:
json.Unmarshal returning blank structure
(1 answer)
Closed 3 years ago.
I am new to Go, and I am trying to practice with building a simple HTTP server. However I met some problems with JSON responses. I wrote following code, then try postman to send some JSON data. However, my postman always gets an empty response and the content-type is text/plain; charset=utf-8. Then I checked a sample in http://www.alexedwards.net/blog/golang-response-snippets#json. I copied and pasted the sample, and it was working well. But I cannot see any difference between mine and the sample. Can someone give some help?
package main
import (
"encoding/json"
"net/http"
)
type ResponseCommands struct {
key string
value bool
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":5432", nil)
}
func handler(rw http.ResponseWriter, req *http.Request) {
responseBody := ResponseCommands{"BackOff", false}
data, err := json.Marshal(responseBody)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
rw.WriteHeader(200)
rw.Header().Set("Content-Type", "application/json")
rw.Write(data)
}
The main difference is that the variable in the struct are public (exported)
type Profile struct {
Name string
Hobbies []string
}
In your case, they are not (lowercase).
type ResponseCommands struct {
key string
value bool
}
See "Lowercase JSON key names with JSON Marshal in Go".
As VonC already answered correct. Just want to add that IDEA can help with such 'small' problems.
I'm using Gogland and it let me know that json tag cannot be applied to lowercase field.
I have a very simple Go webserver. It's job is to receive an inbound json payload. It then publishes the payload to one or more services that expect a byte array. The payload doesn't need to be checked. Just sent over.
In this case, it receives an inbound job and sends it to Google PubSub. It might be another service - it doesn't really matter. I'm trying to find the most efficient way to convert the object to a byte array without first decoding it.
Why? Seems a bit wasteful to decode and convert to JSON on one server, only to unmarshal it later. Plus, I don't want to maintain two identical structs in two packages.
How is it possible to convert the io.ReadCloser to a byte array so I only need to unmarshal once. I tried something like this answer but don't think that's the most efficient way either:
From io.Reader to string in Go
My http server code looks like this:
func Collect(d DbManager) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := 422
obj := Report{}
response := Response{}
response.Message = "Invalid request"
decoder := json.NewDecoder(r.Body)
decoder.Decode(&obj)
if obj.Device.MachineType != "" {
msg,_ := json.Marshal(obj)
if d.Publish(msg, *Topic) {
code = 200
}
response.Message = "Ok"
}
a, _ := json.Marshal(response)
w.WriteHeader(code)
w.Write(a)
return
})
}
You convert a Reader to bytes, by reading it. There's not really a more efficient way to do it.
body, err := ioutil.ReadAll(r.Body)
If you are unconditionally transferring bytes from an io.Reader to an io.Writer, you can just use io.Copy
I'm starting to go crazy trying to get Go to decode this json request body. Here's a sample request:
curl -X POST -d "{\"username\":\"foo\", \"password\":\"bar\"}" http://localhost:3000/users
And here's my handler:
mux.HandleFunc("/users", func(rw http.ResponseWriter, req *http.Request) {
var body struct {
username string
password string
}
// buf := make([]byte, req.ContentLength)
// req.Body.Read(buf)
// fmt.Println(string(buf))
//
// The above commented out code will correctly print:
// {"username":"foo", "password":"bar"}
err := json.NewDecoder(req.Body).Decode(&body)
if err != nil {
rw.WriteHeader(http.StatusNotAcceptable)
return
}
fmt.Printf("%+v\n", body)
// prints -> {username: password:}
})
Like the comment suggests, I can verify that req.Body is indeed correct -- but for whatever reason, json.NewDecoder(req.Body).Decode(&body) never fills out the fields of body.
Any help would be greatly appreciated!
The problem is that the json decoder does not deal with private struct fields. The fields in your body structs are private.
Rewrite it like so and it will work:
var body struct {
Username string `json:"username"`
Password string `json:"password"`
}
basically the json:"username" is a way to tell the json decoder how to map the json name of the object to the struct name. In this instance, for decoding only, it is not necessary - the json decoder is smart enough to make the translation of the upper/lower case.
But if you use the object to encode json as well, you need it, or you'll have upper case field names in the resulting json.
You can use the json struct tags for a few more useful things, like omitting empty field from encoded json, or skipping fields entirely.
You can read more about the JSON struct tags in the documentation for json.Marshal: http://golang.org/pkg/encoding/json/#Marshal