To my shame, I have not been able to figure out how to get data from the frontend in Gin framework. In Django I get data So:
user=request.data.get('user')
print(user)
Everything is simple and understandable as day.
How should I do it in gin?
user := c.Query("user")
user := c.Param("user")
user := c.Params.ByName("user")
user := c.PostForm("user")
println(user)//emptiness....
Well, I'd say you should fetch some book/HOWTO on how HTTP work and spend some time with it because it appears you're trying to bash the problem at hand without actually understanding what happens between your browser and your backend service.
The real problem here is that there are more moving parts that you appear to be aware of, and the way to go depends on what your frontent does.
You did not tell us exactly how you're doing your request,
but from a solicted comment it appears, you're using that "axios" tingy.
If I managed to google that project correctly,
its README states:
By default, axios serializes JavaScript objects to JSON. To send data in the application/x-www-form-urlencoded format instead, you can use one of the following options.
This means two things:
Unless you somehow tweaked the axios' settings, when you did
axios.post, is supposedly performed an HTTP POST request
with its Content-Type field set to application/json
and its payload (or "body" if you prefer) being
a JSON serialization of that {user:this.user} JavaScript object.
It's therefore futile to attempt to parse the query string.
And it's futile to attempt to parse the request as an HTTP form — which it isn't.
Instead, you supposedly want to interpret the incoming request's body as being JSON-formatted. I have no idea as to how to do that in "go-gin", but in plain Go that would be something like
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
var user User
dec := json.NewDecoder(req.Body)
err := dec.Decode(&user)
if err != nil {
rw.Header().Set("Content-Type", "text/plain; charset=UTF-8")
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(rw, "Error parsing request body: ", err)
return
}
}
And ideally you'd first check that the content type of the incoming request was indeed application/json and reject it right away with http.StatusBadRequest if it isn't.
An example of a working code to do that is
// VerifyContentTypeIsJSON makes sure the HTTP header of a server
// http.Request contains the Content-Type field and it indicates
// the request payload is JSON.
// The implementation is based on RFC 7231 (section 3.1.1.5) and RFC 8259.
func VerifyContentTypeIsJSON(header http.Header) error {
var s string
if values := header["Content-Type"]; len(values) > 0 {
s = values[0]
} else {
return errors.New("missing Content-Type")
}
if s == "" {
return errors.New("empty Content-Type")
}
if i := strings.IndexByte(s, ';'); i != -1 {
s = strings.TrimSpace(s[:i])
}
if strings.ToLower(s) != "application/json" {
return fmt.Errorf("unknown Content-Type: %v, must be application/json", s)
}
return nil
}
Having this function, you'd have something like this
after defer req.Body.Close() and actually parsing it:
if err := VerifyContentTypeIsJSON(req.Header); err != nil {
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(rw, err)
return
}
(Note that "go-gin" might have something akin to this already built-in, so please check this.)
The User type should be some struct type matching the shape of the JSON object you intend to unmarshal from the request. Something like this:
type User struct {
User string `json:"user"`
}
None that in the two places my example returned an
error to the user it used content type of plain text
(in UTF-8 encoding). This may be OK but may be not.
Say, your clients might expect a JSON-formatted document
of some agreed-upon shape.
Or you may use content negotiation, but I'd recommend to get simple things straight first.
Literature to check:
HTTP POST request explained at MDN.
URL's query string.
XHR explained at MDN — see also links there.
"Writing Web Applications in Go",
and this in general.
And to maybe answer that part of your question regarding
why it "just worked" in Django.
I can only guess, but I think it merely implements tons of magic which looks at the incoming request and tries to guess how to extract data from it.
The problem is that guessing may indeed work well for
one-off throwaway scripts, but when you're about implementing something like web API (what many not quite correctly call "REST", but let's not digress) it's best
to be very explicit about what your endpoint accept
precisely and how precisely they react to requests — both legitimate and non-well-formed.
Regarding magic in Go, you may read this.
you need to call c.Request.ParseForm() before using it from request.Form
says here:
For all requests, ParseForm parses the raw query from the URL and updates r.Form
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.
If you're expecting a JSON body in the request, you can do it this way with gin. Create a struct for the data you want to grab out of the body. Use json tags for the JSON key names unless you're going to exactly match your Go field names to them. Then call the BindJSON method on the gin context.
For example:
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Login string `json:"login"`
}
func (h *Handler) HandleUser(gctx *gin.Context) {
user := new(User)
err := gctx.BindJSON(user)
if err != nil {
text := fmt.Sprintf("Failed to read user data from request: %s", err)
log.Error(text)
gctx.JSON(http.StatusBadRequest, gin.H{"error": text})
return
}
// do something with user
}
Server GIN can't handle routine default application/json requests from axios!!! What???
Requests should be sent as application/x-www-form-urlencoded.
My decision in Vue project:
Use vue-resource instead axios (axios.post=>this.$http.post) with option
Vue.http.options.emulateJSON = true; in main.js
Related
I'm developing a Go program based on Gin web framework.
I want to serve pictures from the local file system.
func main() {
r := gin.Default()
r.Static("/page2", "resources/pictures")
r.GET("/test", func(context *gin.Context) {
name := context.Query("name")
context.JSON(http.StatusOK, gin.H{
"name": name,
})
})
r.GET("/page", func(context *gin.Context) {
name := context.Query("picname")
context.File("resources/pictures/" + name + ".jpg")
context.JSON(http.StatusOK, "That is "+name)
})
r.Run(":9090")
}
I find it strange that when I use /page2 to get the pictures (url http://localhost:9090/page2/xiamei.jpg), it works fine.
But when I use /page to get the pictures (url http://localhost:9090/page/xiamei.jpg) an error happens.
http: wrote more than the declared Content-Length
What is the internal reason, and what is the root cause of the difference between these two access methods?
In the /page handler, you call both Context.File, and Context.JSON.
The first call to Context.File already writes the file to the response and sets Content-Length header.
By calling Context.JSON again with more payload, you are increasing the actual length of the content without updating the Content-Length header; this results in the error you see.
r.Static doesn't have this issue because it serves one file and doesn't add more payload than it should.
In a Gin handler you should call only one renderer function. If you are serving a file, you can remove the last c.JSON altogether.
I'm trying to proxy a request through Go's net/http package. I copy the http.Request object, send that, and a receive an http.Response object. I want to copy everything from that http.Response object through the http.ResponseWriter back to the client. Something like this, at a glance, would seem to make sense.
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
// Copy req into proxyReq
...
proxyRes, err := http.DefaultClient.Do(proxyReq)
proxyResBody := ioutil.ReadAll(proxyRes.Body)
for header, value := range proxyRes.Header {
w.Header().Set(header, value)
}
w.WriteHeader(proxyRes.StatusCode)
w.Write(proxyResBody)
...
}
However, due to the parsing of the http.Response object, things like the content length header seem to not show up in the proxyRes.Header object. This also neglects possible trailers, and other parsed aspects of the http.Response. I was wondering if there is a simpler/more elegant way to pipe the full (including all headers, cookies, trailers, status code, etc.) http.Response object through the http.ResponseWriter to return everything back to the client, whilst making sure no information is missed. Thanks for the help!
I'm learning go and ran into some design issues while developing web app. The app has main route "/" where user can submit a simple form. With those form values I am calling external API and unmarshaling response into some struct. Now from here I want to make another call based on retrieved values to another external API and I'm not sure what's the proper way of doing this. Here is a snippet for better understandment:
func main() {
http.HandleFunc("/", mainHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
//renders form template
//makes post and retrieves data from api
//here with retrieved data I want to make another call to different API,
// but mainHandler would get too big and complex. I'm not sure how should I pass this data to
// another handler or redirect to another handler with this data.
}
The handlers' semantics should be designed to match the desired HTTP behavior, regardless of the code complexity. If you want to handle a single client request by doing a bunch of stuff, that should be a single handler. If the handler becomes too complex, break it up. Handlers are just functions and can be broken up exactly like any other function - by extracting some part of it into another function and calling that new function. To take you pseudocode:
func mainHandler(w http.ResponseWriter, r *http.Request) {
err := renderTemplate(w)
if err != nil { ... }
err, data := postToApi()
if err != nil { ... }
err, data2 := postToApi2(data)
if err != nil { ... }
}
There's no reason for those functions to be handlers themselves or to get the client involved with a redirect. Just break up your logic the way you normally break up logic - it doesn't matter that it's an HTTP handler.
Hi golearner, in the mainHandler just render the form and make another handler kinda "/formaction" to handle the form, in that way you can easily organize your code.
I'm currently working on an image handler in Go which is intended to reject the upload of unsupported files. My intentions are for the Go program to return an error back via the servers http.ResponceWritter detailing which case for rejection has been met, as json, for the upload service to use.
How errors are set up in the server code:
type Error struct {
ImgHandlerError `json:"error"`
}
type ImgHandlerError struct {
Message string `json:"message"`
Code string `json:"code"`
}
func MakeError(message, code string) *Error {
errorMessage := &Error{
ImgHandlerError: ImgHandlerError{
Message: message,
Code: code,
},
}
return errorMessage
}
func ThrowErr(errorMessage Error, writer http.ResponseWriter) {
jData, err := json.Marshal(errorMessage)
if err != nil {
}
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusForbidden)
writer.Write(jData)
return
}
How an error is generated and written as a HTTP response:
errorMes := *MakeError("PSD files are not supported", "DEF-IMG-0006")
ThrowErr(errorMes, res)
Currently this works as expected, for example when a .psd is uploaded the current HTTP response comes back as JSON.
{"error":{"message":"PSD files are not supported","code":"DEF-IMG-0006"}}
My problem is now testing this as I'm out of my depth, as this is my first Go program. I'm using the Ginko test suite. I've tried setting up the http.ResponseRecorder (as I think thats where the solution lies) in the test suite but I'm having no luck. Also of my current tests in the test suite only test functions which don't require the writer http.ResponseWriter as a param so I'm not sure if I need to start a server in the test suite to have a response writer to read from.
Any help is greatly appreciated
My problem is now testing this as I'm out of my depth, as this is my
first Go program.
So you want to do some end-to-end testing. In your test files you could do something like this.
req := httptest.NewRequest("POST", "my/v1/endpoint", bytes.NewBuffer(payload))
responseRecorder = httptest.NewRecorder()
router.ServeHTTP(responseRecorder, request)
Take a look at httptest.NewRequest and httptest.ResponseRecorder.
Essentially you need to set up your router, personally I use mux and make requests to it just as you would if you were a real consumer of your API.
Your responseRecorder after the request will have fields like Code for reading the response code and things like Body for reading the response body among other fields.
e.g.
if responseRecorder.Code != 200{
t.FailNow()
}
Say I have a html/template like the following:
<html>
<body>
<p>{{SomeFunc .SomeData}}</p>
</body>
and sometimes SomeFunc returns an error. Is there an idiomatic way to deal with this?
If I write directly to the ResponseWriter, then a status code 200 has already been written before I encounter the error.
var tmpl *template.Template
func Handler(w http.ResponseWriter, r *http.Request) {
err := tmpl.Execute(w, data)
// "<html><body><p>" has already been written...
// what to do with err?
}
Preferably I would return a status code 400 or some such, but I can't see a way to do this if I use template.Execute directly on the ResponseWriter. Is there something I'm missing?
Since the template engine generates the output on-the-fly, parts of the template preceding the SomeFunc call are already sent to the output. And if the output is not buffered, they (along with the HTTP 200 status) may already be sent.
You can't do anything about that.
What you can do is perform the check before you call template.Execute(). In trivial case it should be enough to call SomeFunc() and check its return value. If you choose this path and the return value of SomeFunc() is complex, you do not have to call it again from the template, you can simply pass its return value to the params you pass to the template and refer to this value in the template (so SomeFunc() won't have to be executed twice).
If this is not enough or you can't control it, you can create a bytes.Buffer or strings.Builder, execute your template directed into this buffer, and after the Execute() returns, check if there were errors. If there were errors, send back a proper error message / page. If everything went ok, you can just send the content of the buffer to the ResponseWriter.
This could look something like this:
buf := &bytes.Buffer{}
err := tmpl.Execute(buf, data)
if err != nil {
// Send back error message, for example:
http.Error(w, "Hey, Request was bad!", http.StatusBadRequest) // HTTP 400 status
} else {
// No error, send the content, HTTP 200 response status implied
buf.WriteTo(w)
}