Request Body is always empty - go

I am sending a POST request using POSTMAN to a route in my localhost.
The route has been perfectly defined in my main.go file. Moreover, it also gets triggered by sending the post request.
However, when I try to print the body, it always seems to be empty.
I tried to replicated the issue with the code below
package main
import (
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"path"
"strings"
)
func MeetingOperations(w http.ResponseWriter, r *http.Request) {
reqBody, _ := ioutil.ReadAll(r.Body)
fmt.Fprintf(w, "%+v", string(reqBody))
fmt.Println(string(reqBody))
fmt.Println("SCHEDULE MEETING ROUTE")
}
func handleRequests() {
http.HandleFunc("/meetings/", MeetingOperations)
log.Fatal(http.ListenAndServe(":10000", nil))
}
func main() {
handleRequests()
}
Yet the body is logged as empty in the console.
I have tried sending a request using curl with no success. I also tried creating an HTML template form and submitting it with no luck.
Any sort of help or explanation is highly appreciated.

You have a trailing slash in your route definition, but in your Postman request its missing. If you use a library, you can typically handle this case.

Related

Go Mux CORS error with both gorilla/handlers and rs/cors [duplicate]

I have fairly simple setup here as described in the code below. But I am not able to get the CORS to work. I keep getting this error:
XMLHttpRequest cannot load http://localhost:3000/signup. Response to
preflight request doesn't pass access control check: No 'Access-
Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8000' is therefore not allowed access. The
response had HTTP status code 403.
I am sure I am missing something simple here.
Here is the code I have:
package main
import (
"log"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
log.Fatal(http.ListenAndServe(":3000", handlers.CORS()(router)))
}
Please read the link Markus suggested, and also about what triggers CORS pre-flight requests.
Pre-flight requests: You may have a content type like JSON, or some other custom header that's triggering a pre-flight request, which your server may not be handling. Try adding this one, if you're using the ever-common AJAX in your front-end: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requested-With
Gorilla's handlers.CORS() will set sane defaults to get the basics of CORS working for you; however, you can (and maybe should) take control in a more functional manner.
Here's some starter code:
// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
originsOk := handlers.AllowedOrigins([]string{os.Getenv("ORIGIN_ALLOWED")})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
// start server listen
// with error handling
log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
You can get more details here: Why doesn’t Postman get a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error when my JavaScript code does? about this issue.
Also try this handler: Go Cors Handler which should solve your issue. I find this much cleaner and easy to resolve the issue.
package main
import (
"log"
"net/http"
"github.com/rs/cors"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
c := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:8000"},
AllowCredentials: true,
})
handler := c.Handler(router)
log.Fatal(http.ListenAndServe(":3000", handler)
}
You should create a CORSOption object. For example to allow any origin, Use this code:
corsObj:=handlers.AllowedOrigins([]string{"*"})
Then you pass this object to your handle.CORS function:
log.Fatal(http.ListenAndServe(":3000", handlers.CORS(corsObj)(router)))
For testing it you can use CURL:
curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose http://127.0.0.1:3000
When it works you should see those headers:
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With
Final code is here: https://play.golang.org/p/AOrlJsWhvf
More info:
AllowedOrigin function
How can you debug a CORS request with cURL?
I realize this is an old issue but nonetheless it took me 30min to get this right.
handler = handlers.CORS(
// handlers.AllowedMethods([]string{"GET", "POST", "PUT"}),
handlers.AllowedHeaders([]string{"Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"}),
// handlers.AllowedOrigins([]string{"*"}),
)(handler)
Things to note:
AllowedMethods does NOT need to explicitly include OPTIONS, this is part of the CORS handler
AllowedHeaders need to be explicitly mentioned, * is not a valid wildcard. Typical ajax libraries will send Content-Type when requesting something like application/json, so add that as well.
* is the default for AllowedOrigin
After declaring the mux object, add the accessControlMiddleware as a middleware to the declared object.
func main(){
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.Use(accessControlMiddleware)
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
http.ListenAndServe(":3000", corsOpts.Handler(router))
}
// access control and CORS middleware
func accessControlMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT")
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
if r.Method == "OPTIONS" {
return
}
next.ServeHTTP(w, r)
})
}
package main
import (
"log"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
"github.com/rs/cors"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
//cors optionsGoes Below
corsOpts := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:8100"}, //you service is available and allowed for this base url
AllowedMethods: []string{
http.MethodGet,//http methods for your app
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodOptions,
http.MethodHead,
},
AllowedHeaders: []string{
"*",//or you can your header key values which you are using in your application
},
})
http.ListenAndServe(":3000", corsOpts.Handler(router))
}
Base on jeremiah.trein's answer.
CORS filters are set on server side. Request may work with Postman and fail with a browser because Postman doesn't send preflight request whereas a browser does.
Setting the CORS filters will allow you to configure the origins, methods and headers that the backend shall accept.
In addition, if your browser emits POST or PUT requests that contain a json payload (which is quite reasonnable), you'll need to add 'Content-Type' to the allowed headers.
Finally the handlers.CORS()(router) does not only work with the http.ListenAndServe function but also with http.Handle().
The snippet of code might as well look like:
router := mux.NewRouter()
// do all your routes declaration
headersOK := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOK := handlers.AllowedOrigins([]string{"*"})
methodsOK := handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS", "DELETE", "PUT"})
http.Handle("/", handlers.CombinedLoggingHandler(os.Stderr, handlers.CORS(headersOK, originsOK, methodsOK)(router)))
It is worth mentionning that i have successfuly used this snippet of code in a Google Cloud Platform Standard AppEngine (and I believe it would work in a Flex AppEngine as well).
The aformentioned package github.com/rs/cors provides a constructor
AllowAll() *Cors
that
...create a new Cors handler with permissive configuration allowing all
origins with all standard methods with any header and credentials.

HTTP client receives status code 200 when server panics with gin-gonic and gin-contrib/gzip

When accessing the gin-gonic server below, the HTTP client should receive the code 500, but receives the code 200.
package main
import (
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
func main() {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(gzip.Gzip(gzip.DefaultCompression))
r.POST("/test", func(c *gin.Context) {
panic("test") // Server panic and client should receive code 500.
})
r.Run(":8080")
}
When accessing /test from a HTTP client, the go server log is as below and looks return the code 500.
[GIN] 2020/09/28 - 10:23:14 | 500 | 67.2995ms | ::1 | POST "/test"
2020/09/28 10:23:14 [Recovery] 2020/09/28 - 10:23:14 panic recovered:
test
C:/path/to/myproject/main.go:16 (0x8f193f)
main.func1: panic("test")
But HTTP client receives the code 200.
When I remove r.Use(gzip.Gzip(gzip.DefaultCompression)), the HTTP client receives the code 500.
Why the client receives code 200 with r.Use(gzip.Gzip(gzip.DefaultCompression)), How can I fix this?
I've reproduce your case. Postman got code 200 but the server results 500 instead.
The server will call c.Next() to execute 4 handlers when receive post requests. The sequence is as follow:
gin.Logger
gin.Recovery
gzip.Gzip(gzip.DefaultCompression)
your handler
Here is gin responseWriter writes response header and it will write header only once.
func (w *responseWriter) WriteHeaderNow() {
if !w.Written() {
w.size = 0
w.ResponseWriter.WriteHeader(w.status)
}
}
Both gzip.Gzip(gzip.DefaultCompression) and gin.Recovery has defer func to write response header. Golang's deferred calls are executed in last-in-first-out order. So gzip.Gzip(gzip.DefaultCompression) will write response header to 200, and gin.Recovery won't write reponse header to 500 as expected.
So to solve this problem, you should change the order of handlers and make sure gin.Recovery is the last handler to load.
Adding the recovery middleware last seems to fix this.
package main
import (
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
func main() {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Logger())
r.Use(gzip.Gzip(gzip.DefaultCompression))
r.Use(gin.Recovery())
r.POST("/test", func(c *gin.Context) {
panic("test") // Server panic and client should receive code 500.
})
r.Run(":8080")
}

How do I redirect a GET request to a POST request with some data?

When a user hits a certain url with a GET request I'd like to redirect them to a POST request at another location.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func old(w http.ResponseWriter, r *http.Request) {
newURL := "/new"
var bdy = []byte(`title=Buy cheese and bread for breakfast.`)
r.Method = "POST"
r.URL, _ = url.Parse(newURL)
r.RequestURI = newURL
r.Body = ioutil.NopCloser(bytes.NewReader(bdy))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
http.Redirect(w, r, newURL, 302)
}
func new(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Printf("Method:%v\n", r.Method)
fmt.Printf("Title:%v\n", r.Form.Get("title"))
}
func main() {
http.HandleFunc("/", old)
http.HandleFunc("/new", new)
port := 8000
fmt.Printf("listening on %v\n", port)
if err := http.ListenAndServe(fmt.Sprintf(":%v", port), nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
When I hit "/" I end up getting redirected to "/new" but with a GET request and no form data:
Method:GET
Title:
If I curl "/new" directly I get :
curl -XPOST localhost:8000/new -d "title=Buy cheese and bread for breakfast."
Method:POST
Title:Buy cheese and bread for breakfast.
A HTTP redirect (i.e. reply with status code 301, 302, 307,308 and Location header) can only redirect the existing request to another location and not change the payload of the request. It can add some cookies in the response header though.
In order to automatically change a GET request into a POST request with a specific payload you might try to send the client a HTML page with a <form method=POST... and the payload with hidden input fields, i.e. <input name=... value=... type=hidden> and then add some JavaScript to the page which automatically submits the form. But this kind of hack will only work in browsers and only if JavaScript is enabled and will not work with all kind of payloads either.
To keep compatibility with a broader range of clients it is probably better to design it differently, i.e. keep the GET request in the redirect but give the necessary payload as a parameter to the new target, i.e. http://new.target/foo?payload=..... But the details depend on what the target of the request can deal with.
Unfortunately I don't believe a redirect can change the verb (e.g., GET, POST) or add data to the request. It can only change the URL.
See Redirect () for more information.
I've never heard about changing verb from GET to POST. I guess it's impossible because POST supposes body of body (however may be empty) and GET doesn't. So in general case browser would not be able to take the body from nothing.
Otherwise is possible: you may send 302 redirect after post to make browser perform get. Also verb can be kept with 307 reply code.
Try to rethink browser-server interaction. May be you can redirect POST to another location to solve a task?

Why isn't the Response field of this HTTP Request being populated?

The comment for the field Response in the type http.Request is as follows.
// Response is the redirect response which caused this request
// to be created. This field is only populated during client
// redirects.
Response *Response
However, it seems to me that this field is not being populated during requests, as it is implied that it is. Consider the following example:
package main
import (
"net/http"
"log"
"fmt"
)
func handleA(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/b", http.StatusSeeOther)
}
func handleB(writer http.ResponseWriter, request *http.Request) {
fmt.Println(request.Response)
}
func main() {
http.HandleFunc("/a", handleA)
http.HandleFunc("/b", handleB)
log.Fatal(http.ListenAndServe(":8080", nil))
}
If I compile and run this code and navigate to localhost:8080/a, then I get redirected to localhost:8080/b and the server prints <nil> to the console. But shouldn't it be printing out a non-nil value, since the request is coming as the result of a redirect?
In your sample, the redirect is happening in the browser; the server doesn't get to know what response generated the redirect. That field is populated when making HTTP requests from a Go app; for example, if you use http.Client to request a URL, and the response is a redirect, it generates a new Request for the redirect URL, and in that Request, the Response field will be populated with the response that triggered that redirect.
This is evidenced in the source for http.Client: https://golang.org/src/net/http/client.go#L669

Making golang Gorilla CORS handler work

I have fairly simple setup here as described in the code below. But I am not able to get the CORS to work. I keep getting this error:
XMLHttpRequest cannot load http://localhost:3000/signup. Response to
preflight request doesn't pass access control check: No 'Access-
Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8000' is therefore not allowed access. The
response had HTTP status code 403.
I am sure I am missing something simple here.
Here is the code I have:
package main
import (
"log"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
log.Fatal(http.ListenAndServe(":3000", handlers.CORS()(router)))
}
Please read the link Markus suggested, and also about what triggers CORS pre-flight requests.
Pre-flight requests: You may have a content type like JSON, or some other custom header that's triggering a pre-flight request, which your server may not be handling. Try adding this one, if you're using the ever-common AJAX in your front-end: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requested-With
Gorilla's handlers.CORS() will set sane defaults to get the basics of CORS working for you; however, you can (and maybe should) take control in a more functional manner.
Here's some starter code:
// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
originsOk := handlers.AllowedOrigins([]string{os.Getenv("ORIGIN_ALLOWED")})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
// start server listen
// with error handling
log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
You can get more details here: Why doesn’t Postman get a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error when my JavaScript code does? about this issue.
Also try this handler: Go Cors Handler which should solve your issue. I find this much cleaner and easy to resolve the issue.
package main
import (
"log"
"net/http"
"github.com/rs/cors"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
c := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:8000"},
AllowCredentials: true,
})
handler := c.Handler(router)
log.Fatal(http.ListenAndServe(":3000", handler)
}
You should create a CORSOption object. For example to allow any origin, Use this code:
corsObj:=handlers.AllowedOrigins([]string{"*"})
Then you pass this object to your handle.CORS function:
log.Fatal(http.ListenAndServe(":3000", handlers.CORS(corsObj)(router)))
For testing it you can use CURL:
curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose http://127.0.0.1:3000
When it works you should see those headers:
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With
Final code is here: https://play.golang.org/p/AOrlJsWhvf
More info:
AllowedOrigin function
How can you debug a CORS request with cURL?
I realize this is an old issue but nonetheless it took me 30min to get this right.
handler = handlers.CORS(
// handlers.AllowedMethods([]string{"GET", "POST", "PUT"}),
handlers.AllowedHeaders([]string{"Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"}),
// handlers.AllowedOrigins([]string{"*"}),
)(handler)
Things to note:
AllowedMethods does NOT need to explicitly include OPTIONS, this is part of the CORS handler
AllowedHeaders need to be explicitly mentioned, * is not a valid wildcard. Typical ajax libraries will send Content-Type when requesting something like application/json, so add that as well.
* is the default for AllowedOrigin
After declaring the mux object, add the accessControlMiddleware as a middleware to the declared object.
func main(){
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.Use(accessControlMiddleware)
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
http.ListenAndServe(":3000", corsOpts.Handler(router))
}
// access control and CORS middleware
func accessControlMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT")
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
if r.Method == "OPTIONS" {
return
}
next.ServeHTTP(w, r)
})
}
package main
import (
"log"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"myApp/src/controllers"
"github.com/rs/cors"
)
func main() {
ac := new(controllers.AccountController)
router := mux.NewRouter()
router.HandleFunc("/signup", ac.SignUp).Methods("POST")
router.HandleFunc("/signin", ac.SignIn).Methods("POST")
//cors optionsGoes Below
corsOpts := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:8100"}, //you service is available and allowed for this base url
AllowedMethods: []string{
http.MethodGet,//http methods for your app
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodOptions,
http.MethodHead,
},
AllowedHeaders: []string{
"*",//or you can your header key values which you are using in your application
},
})
http.ListenAndServe(":3000", corsOpts.Handler(router))
}
Base on jeremiah.trein's answer.
CORS filters are set on server side. Request may work with Postman and fail with a browser because Postman doesn't send preflight request whereas a browser does.
Setting the CORS filters will allow you to configure the origins, methods and headers that the backend shall accept.
In addition, if your browser emits POST or PUT requests that contain a json payload (which is quite reasonnable), you'll need to add 'Content-Type' to the allowed headers.
Finally the handlers.CORS()(router) does not only work with the http.ListenAndServe function but also with http.Handle().
The snippet of code might as well look like:
router := mux.NewRouter()
// do all your routes declaration
headersOK := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOK := handlers.AllowedOrigins([]string{"*"})
methodsOK := handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS", "DELETE", "PUT"})
http.Handle("/", handlers.CombinedLoggingHandler(os.Stderr, handlers.CORS(headersOK, originsOK, methodsOK)(router)))
It is worth mentionning that i have successfuly used this snippet of code in a Google Cloud Platform Standard AppEngine (and I believe it would work in a Flex AppEngine as well).
The aformentioned package github.com/rs/cors provides a constructor
AllowAll() *Cors
that
...create a new Cors handler with permissive configuration allowing all
origins with all standard methods with any header and credentials.

Resources