Unable to set query parameters manually for a rest api (using mux) - go

I am implementing a Rest api using mux. After validating some parameters, I am trying to fill the missing parameters with some default values which I process later by the method that handles the request, however, I noticed that setting query parameters manually does not have any effect, unless the raw query is directly changed which is a bit hacky:
func ValidateParameters(r *http.Request) (bool) {
test := r.URL.Query().Get("test")
// if test is not provided set some default value
if test == "" {
r.URL.Query().Set("test", "Test1") //not working
r.URL.Query().Add("test", "Test2") //not working
r.URL.RawQuery = r.URL.RawQuery + "&Test=Test3" // the only method working
}
// more code
}
The handler is in another file, so I want to be able to do test := r.URL.Query().Get("test") and get the value that I set inside ValidateParameters which is called by the request handler, but this is not working.
any explanation to that ? any way to work around it ?
Thanks.

The problem is that r.URL.Query() parses the url, creates the map of parameters and returns it. This is done with every .Query() call.
This should work:
params := r.URL.Query()
params.Set("key", "value")
r.URL.RawQuery = params.Encode()

Related

How to set data in gin request context?

I have a very simple piece of code. One single middleware is applied to all routes. In this middleware, the header field 'x-sentinel-tenant' is read. If it is blank, an error is returned. If it has some value, then that value is to be extracted and set in the request context for later use.
Here's my middleware code
// VerifyTenant ensures that the user has added tenant information in the header
func VerifyTenant(c *gin.Context) {
requestTenant := c.Request.Header.Get("x-tenant")
if requestTenant == "" {
c.AbortWithStatusJSON(
http.StatusBadRequest,
views.GenerateErrorResponse(
http.StatusBadRequest,
"Please add tenant in the header.",
c.Request.URL.Path,
),
)
return
}
c.Request = c.Request.WithContext(context.WithValue(c, tenant, requestTenant))
}
When I try to access this context in a later part of my code, here's how I get the data when hovered over the ctx value.
I am not sure what am I doing wrong. It's a fairly straight forward piece of code which is behaving very funny. Any leads will be appreciated. Thank you
When I update the context, I expect "tenant" to be added as another field. Which is not happening. Can multiple values not coexist in the same context?
My question - how to correctly set multiple values in gin request context
Here's how I am trying to access tenant
return ctx.Value("Context").(context.Context).Value("tenant").(string)
https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#Context.Set
func (*Context) Set does exactly what you want. an example is:
func VerifyTenant(ctx *gin.Context) {
requestTenant := ctx.GetHeader("x-tenant")
if requestTenant == "" {
// Abort
return
}
ctx.Set("x-tenant", requestTenant)
}
func Test(ctx *gin.Context){
// when you need to get the value of "x-tenant"
requestTenant := ctx.GetString("x-tenant")
}

Code design for handle funcs in go web app

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.

How get DATA from frontend in gin?

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

How to use custom http handlers/middleware if headers already set?

I'm trying to chain HTTP handlers in go to provide some added functionality, like this:
package router
import (
// snip
"github.com/gorilla/mux"
"github.com/gorilla/handlers"
"net/http"
)
// snip
r := mux.NewRouter()
/* routing code */
var h http.Handler
h = r
if useGzip {
h = handlers.CompressHandler(h)
}
if useLogFile {
fn := pathToLog
accessLog, err := os.OpenFile(fn, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
panic(err)
}
h = handlers.CombinedLoggingHandler(accessLog, h)
}
// etc...
The problem is, if any HTTP headers are already set by one of the controllers that the gorilla/mux router points to (for example, w.WriteHeader(404) or w.Header().Set("Content-Type", "application/json")) - this silently breaks any "wrapper" handler trying to set or add its own headers, like the compress handler. I can't see any errors, unless I forgot to catch one somewhere, but the browser gets an invalid response.
Is there any graceful way to deal with this, short of just stashing the headers somewhere and then leaving the final handler to write them? It seems like that would mean rewriting the handlers' code, which I'd love to avoid if at all possible.
Once you call w.WriteHeader(404), the header goes on a wire. So you can't add to it anymore.
Best way you can do is to buffer status code and write it at the end of a chain.
For example, you can provide your own wrapper for http.ResponseWriter that would re-implement WriteHeader() to save status value. Then add method Commit() to actually write it.
Call Commit() in the last handler. You have to determine somehow which handler is last, of course.
I experienced the same silently-failing behaviour. But only in handlers where I did WritheHeader to set a status code other than StatusOK. I think things went wrong in this part of CompressHandler:
if h.Get("Content-Type") == "" {
h.Set("Content-Type", http.DetectContentType(b))
}
Which appears to be resolved when explicitly setting the content type in my own handler:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(code)

Golang with couchbase integration issue

I'm using golang with couchbase integration component called go-couchbase. It's enable to connect with couchbase and retrieve data. However I have a problem to send start key and skip value and limit value with this API. Because there is no functionality found by myself.
url : - github.com/couchbaselabs/go-couchbase
Please let me know any method to send these values to couchbase and retrieve data?
That start key is only mentioned once, as a parameter to a couhbase view:
// View executes a view.
//
// The ddoc parameter is just the bare name of your design doc without
// the "_design/" prefix.
//
// Parameters are string keys with values that correspond to couchbase
// view parameters. Primitive should work fairly naturally (booleans,
// ints, strings, etc...) and other values will attempt to be JSON
// marshaled (useful for array indexing on on view keys, for example).
//
// Example:
//
// res, err := couchbase.View("myddoc", "myview", map[string]interface{}{
// "group_level": 2,
// "start_key": []interface{}{"thing"},
// "end_key": []interface{}{"thing", map[string]string{}},
// "stale": false,
// })
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
I suppose the skip one (mentioned in "Pagination with Couchbase") is just another parameter to add to the params map[string]interface{}.

Resources