Access POST Request Value in FormValue Golang using POSTMAN - go

I have no idea why I always receive an empty string when sending values in POSTMAN
func main(){
rtr := mux.NewRouter()
rtr.HandleFunc("/search", search).Methods("POST")
}
func search(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name") //returns empty
}
This is the body request in POSTMAN
screenshot for the body request
{
"name": "markus"
}
I tried to change the body request to form data
Screenshot for form data in post request
But it still didn't work.
Does anyone have a solution?
Thanks

What you have there is not a FormValue but a JSON body. If your JSON object is just a simple map of string to string, then you can do something like this:
func search(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body) // check for errors
keyVal := make(map[string]string)
json.Unmarshal(body, &keyVal) // check for errors
name := keyVal["name"]
// do whatever with name
}
Edit
If you need to parse a form value you need to call ParseForm()
func search(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
// handle err
}
name := r.FormValue("name")
}

Just want to share additional information here.
Please check the Content-Type in Header section of Postman if you are facing any problem in sending your request to the server.
Set Content-Type to application/json for sending raw JSON in request.
Set Content-Type to application/x-www-form-urlencoded if you are sending form values in request. Also select x-www-form-urlencoded in postman's Body section

Related

Cannot bind POST body to URL in Go

I'm trying to make a simple API call to the pokemon API through reaching a POST request that I'm serving with Echo.
I'm sending a POST request to "localhost:8000/pokemon" with the body { "pokemon": "pikachu" } where the BODY is reattached to the request through ioutil changing the request to be made with the body: "localhost:8000/pokemon/pikachu".
The POST request works by responding with some JSON, but the call being made is only to "localhost:8000/pokemon", and it seems the body isn't added to the URL.
I think there is something wrong with the binding here u := new(pokemon)
Anyone have any ideas?
func main() {
e := echo.New() // Middleware
e.Use(middleware.Logger()) // Logger
e.Use(middleware.Recover())
//CORS
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}))
// Root route => handler
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
})
e.POST("/pokemon", controllers.GrabPrice) // Price endpoint
// Server
e.Logger.Fatal(e.Start(":8000"))
}
type pokemon struct { pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}
// GrabPrice - handler method for binding JSON body and scraping for stock price
func GrabPrice(c echo.Context) (err error) {
// Read the Body content
var bodyBytes []byte
if c.Request().Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request().Body)
}
// Restore the io.ReadCloser to its original state
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
u := new(pokemon)
er := c.Bind(u) // bind the structure with the context body
// on no panic!
if er != nil {
panic(er)
}
// company ticker
ticker := u.pokemon
print("Here", string(u.pokemon))
// yahoo finance base URL
baseURL := "https://pokeapi.co/api/v2/pokemon"
print(baseURL + ticker)
// price XPath
//pricePath := "//*[#name=\"static\"]"
// load HTML document by binding base url and passed in ticker
doc, err := htmlquery.LoadURL(baseURL + ticker)
// uh oh :( freak out!!
if err != nil {
panic(err)
}
// HTML Node
// from the Node get inner text
price := string(htmlquery.InnerText(doc))
return c.JSON(http.StatusOK, price)
}
Adding to what already answered by #mkopriva and #A.Lorefice
Yes you need to ensure that the variable are exported, for the binding to work properly.
Since underlay process of binding actually using reflection mechanism on the struct. See this documentation, scroll into Structs section to see what it is.
type pokemon struct {
Pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}

Can you return json in golang http.Error?

Can you return json when http.Error is called?
myObj := MyObj{
MyVar: myVar}
data, err := json.Marshal(myObj)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(data)
w.Header().Set("Content-Type", "application/json")
http.Error(w, "some error happened", http.StatusInternalServerError)
I see that it returns 200 with no json but the json is embed in text
I've discovered that it's really easy to read the Go source. If you click on the function in the docs, you will be taken to the source for the Error function: https://golang.org/src/net/http/server.go?s=61907:61959#L2006
// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
fmt.Fprintln(w, error)
}
So if you want to return JSON, it's easy enough to write your own Error function.
func JSONError(w http.ResponseWriter, err interface{}, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
json.NewEncoder(w).Encode(err)
}
It should be plain text only.
From docs
func Error(w ResponseWriter, error string, code int)
Error replies to the request with the specified error message and HTTP
code. It does not otherwise end the request; the caller should ensure
no further writes are done to w. The error message should be plain
text.
Also I think your usage of http.Error is not correct. When you call w.Write(data), the response is sent and response body will be closed. That is why you are getting 200 status instead of 500 from http.Error.
Instead of using http.Error, you can send your own error response with json just like how you would send any other response by setting the status code to an error code.
Like #ShashankV said, you are writing the response in a wrong way.
As an example, the following is what I did during learning about writing RESTful API serving in Golang:
type Response struct {
StatusCode int
Msg string
}
func respond(w http.ResponseWriter, r Response) {
// if r.StatusCode == http.StatusUnauthorized {
// w.Header().Add("WWW-Authenticate", `Basic realm="Authorization Required"`)
// }
data, err := json.Marshal(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, err.Error())
return
}
w.WriteHeader(r.StatusCode)
fmt.Fprintf(w, r.Msg)
}
func Hello(w http.ResponseWriter, r *http.Request) {
resp := Response{http.StatusOK, welcome}
respond(w, resp)
}
Ref: https://github.com/shudipta/Book-Server/blob/master/book_server/book_server.go
Hope, this will help.
My answer is a bit late and there are some good answers already. Here are my 2 cents.
If you want to return JSON in case of error there are multiple ways to do so. I can list two:
Write your own Error handler method
Use the go-boom library
1. Writing your own error handler method
One way is what #craigmj has suggested, i.e. create your own method, for eg.:
func JSONError(w http.ResponseWriter, err interface{}, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
json.NewEncoder(w).Encode(err)
}
2. Use the go-boom library
Another approach is using the go-boom library. For eg., in case the err relates to resource not found, you can do:
err := errors.New("User doesn't exist")
...
boom.NotFound(w, err)
And the response will be:
{
"error": "Not Found",
"message": ",
"statusCode": 404
}
For more check the documentation of the go-boom.
Hope that helps.

How do I get the POST values from this request?

I have the following code:
package main
import (
"bytes"
"fmt"
"net/http"
)
func main() {
var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
req, err := http.NewRequest("POST", "/", bytes.NewBuffer(jsonStr))
if err != nil {
panic(err)
}
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
req.ParseForm()
fmt.Printf("%v:%v", "title", req.Form.Get("title"))
}
I am unable to extract the "title" param and not sure why.
As noted in the GoDoc for the http.Request.ParseForm method, the type of the body must be application/x-www-form-urlencoded, not JSON like your current example:
For other HTTP methods, or when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value.
Here is an updated example of your code using a form body, which gives the intended result: https://play.golang.org/p/Zrw05T2Zb5Z
If you want to extract values from a JSON body, that can be done using a method such as json.Unmarshal, however a JSON body doesn't represent a form.
The 3rd argument of http.NewRequest is the http payload.
In your case, payload type is application/json. It's need to be treated as json, only then you'll be able to get certain value from the it. In this case, we just cannot use the same technique like on getting value from query string or form data.
So just unmarshal the jsonStr data into map or struct.
res := make(map[string]interface{})
err := json.Unmarshal(jsonStr, &res)
if err != nil {
panic(err)
}
fmt.Printf("%#v \n", res["title"])
To be honest I'm quite confused with your question, why you need to get the payload from http client request.
If what you want is actually how to get the payload from the web server end, you can get it by decoding the request body. Example:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
payload := make(map[string]interface{})
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
title := payload["title"].(string)
w.Write([]byte(title))
})
Curl example (based on your code):
curl -d '{"title":"Buy cheese and bread for breakfast."}' \
-H "Content-Type: application/json" \
-X POST http://localhost:9000
Output:
Buy cheese and bread for breakfast.
Because your request isn't a form. It doesn't have any GET parameters, and it isn't form-encoded data.
For other HTTP methods, or when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value.
[ https://golang.org/pkg/net/http/#Request.ParseForm ]
You're free to parse the body of the request as application/json, but that isn't the same as form data.

How to log response body in gin while I redirect the initial request

I'm trying to log the response body of a request that has been redirected.
func main() {
r := gin.Default()
eanAPI := api.NewEanAPI()
v1 := r.Group("/v1")
v1.POST("/*action", eanAPI.Redirect, middleware.SaveRequest())
port := os.Getenv("PORT")
if len(port) == 0 {
port = "8000"
}
r.Run(":" + port)
}
func (api *eanAPI) Redirect(ctx *gin.Context) {
forwardToHost := "https://jsonplaceholder.typicode.com"
url := ctx.Request.URL.String()
ctx.Redirect(http.StatusTemporaryRedirect, forwardToHost)
}
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func SaveRequest() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
statusCode := c.Writer.Status()
fmt.Println("status: ", statusCode)
if statusCode <= 400 {
//ok this is an request with error, let's make a record for it
// now print body (or log in your preferred way)
fmt.Println("Response body: " + blw.body.String())
}
}
Unfortunately, the body response is always empty. Maybe the redirection or the middleware is messing with my body response
When I tried with postman I can see the body response coming! You can try it by a POST on https://jsonplaceholder.typicode.com/posts. It should return a payload with an id in the body response
What am I doing wrong here?
Thanks in advance
The response body you're seeing in the browser/postman is from the page you're being redirected to. The page doing the actual redirecting likely has no response body, just a status and a Location header. This is the expected behavior of a redirect. If you want to try to capture the body of the page you're redirecting to, you can't actually use redirects for that (the redirect handler isn't involved in the final request); you'd have to fully proxy the request rather than redirecting it. Proxying is very different from redirecting though, so make sure that's really the behavior you're after.

How to send post data value in go-wrk command?

In curl, I can send post data by -d flag like below example
curl -X POST -d'{"accountID":"1"}' localhost:1234/geInfo
How am I supposed to send accountID value in go-wrk command for a post request?
Unless I am mistaken, it is (currently) not supported to pass post parameters.
I figured that from the code of go-wrk by following the -m="POST" parameter which suggests otherwise. (Offering the method "POST" of course does not imply you can also pass parameters)
The parameter is parsed in main.go:19:
method = flag.String("m", "GET", "the http request method")
then passed on to client in single_node.go:16:
go StartClient(
toCall,
*headers,
*method,
*disableKeepAlives,
responseChannel,
wg,
*totalCalls,
)
where it is received in third place in "meth" variable (client.go:14):
func StartClient(url_, heads, meth string, dka bool, responseChan chan *Response, waitGroup *sync.WaitGroup, tc int) {
and then used here (client.go:55):
req, _ := http.NewRequest(meth, url_, nil)
sets := strings.Split(heads, "\n")
//Split incoming header string by \n and build header pairs
for i := range sets {
split := strings.SplitN(sets[i], ":", 2)
if len(split) == 2 {
req.Header.Set(split[0], split[1])
}
}
timer := NewTimer()
for {
timer.Reset()
resp, err := tr.RoundTrip(req)
respObj := &Response{}
(...)
responseChan <- respObj
}
If post parameters would be passable, they would have to be put somewhere into the Request as you can lookup on the golang http package website:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest returns a new Request given a method, URL, and optional body.

Resources