How to use multiple parameters in query router - go

everybody!
The question is:
How to write multiple parameters in query router, so I can write one, two or more parameters like this:
/applications/filter/?date=today
/applications/filter/?status=true
/applications/filter/?date=today&status=true
I tried this, but it does not work for single parameter, only for two:
router.HandleFunc("/applications/filter/", authMiddle.RequiresLogin(authContrl.FilterDateStatus())).
Queries("date", "{date}", "status", "{status}").Methods("GET")

This is a little bit confusing in the beginning, but your route is always the same here:
/applications/filter/?date=today
/applications/filter/?status=true
/applications/filter/?date=today&status=true
It is always /applications/filter/.
In that case you just need to map one route here. The handle func receives the request. Inside the request you can parse the url.
https://play.golang.org/p/op49nTJSlCP
Putting all together it could look like:
router.HandleFunc("/applications/filter/",func(w http.ResponseWriter,r *http.Request){
// in production you should handle the errors!
// I am just skipping this to keep the example simple
u, _ := url.Parse(r.URL)
v := u.Query()
if _,ok := v[date]; ok {
// do something with dae
}
})

Related

How do I get the URL before routing?

I want to get the url or the neme of the route before routing it, ex:
router.HandleFunc("/person", person.HandlePerson)
router.HandleFunc("/animal", animal.HandleAnimal)
I want to know the route before routing like this:
nameOfRoute:= //I want this variable to store the name of the route (person or animal) before routing
fmt.Println(nameOfRoute) //This line will print the name of the route ("/animal", "/person")
router.HandleFunc("/person", person.HandlePerson)
router.HandleFunc("/animal", animal.HandleAnimal)
How can I do this in Golang
You cannot — in the sense that you showed. That's because the statements
router.HandleFunc("/person", person.HandlePerson)
are not actions which carry out actual routing but rather directives to the router about how it should do routing.
The routing itself is handler via a single "entry" function of your router.
What you can do, however, is to employ "cascading" of several routers:
Create a router which handles /, and then create another router which handles all your indiviual routes — as it's currently done.
Make the sole handler function of the first — root — router print the URL and then merely call the inner's router entry function.
I have no idea which router package are you using (there are gazillions of them, including the stock one) so I cannot tell you what that function is; in a stock router it's ServeHTTP.
Please consider figuring this out as a homework ;-)
With a stock net/http's ServeMux, you do not even need a root router because a single function would do — something like this:
mux := http.NewServeMux()
mux.Handle("/person", whatever)
...
server := http.Server{
Handler: func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
mux.ServeHTTP(w, r)
},
}
server.ListenAndServe()

Golang Mocking a call with some parameters in body with values not fixed

I am mocking a method call as follows:
tctx.someMock.On("addProd",
product.NewAddProductParamsWithContext(ctx).
WithID("someid").
WithCreateRequest(pro.CreateProdBody{
creationDate: "someDate" ,
}), nil).
Return(nil, nil)
which works fine.
Now, here, instead of passing a fixed value for the field creationDate, if I want to generalize it so it will work for any value passed, how can I achieve that? I am pretty new to Go, so not sure how to do this
the values for creationDate could be any value like - 2021-03-19T18:57:16.589Z or 2022-04-23T14:17:56.589Z etc. I just dont want to limit the mock call to work for a fixed value of creationDate, but I would like it to work for any date string passed
Assuming you're using github.com/stretchr/testify/mock, you should be able to use mock.MatchedBy() to match on specific parts of the argument. For example:
tctx.someMock.On("addProd", mock.MatchedBy(func(i interface{}) bool {
p := i.(*product.AddProductParams)
return p.ID() == "someid"
})).Return(nil, nil)
However, I find this to be most useful when needing to take a different action depending on the input. If you're simply verifying addProd was called with a specific argument, consider asserting that instead:
tctx.someMock.On("addProd", mock.Anything).Return(nil, nil)
...
tctx.someMock.AssertCalled(t, "addProd", mock.MatchedBy(func(i interface{}) bool {
p := i.(*product.AddProductParams)
return p.ID() == "someid"
})).Return(nil, nil)

How to implement sub-routes

I want to achieve routes like
user/profile
user/cart
user/products
Currently, I'm doing this
r.HandleFunc("user/signup", signupHandler).Methods("POST")
r.HandleFunc("user/signin", signinHandler).Methods("POST")
r.HandleFunc("user/profile", profileHandler).Methods("GET")
r.HandleFunc("user/cart", cartHandler).Methods("POST")
r.HandleFunc("user/products", productsHandler).Methods("GET")
As you can see these routes starts with user so can how can I know it's a user routes so I can send handle it in a different file.
I want something like
r.HandleFunc("user/", handlerWhichHandelAllTheRequestFromUser)
It should handle all the URL which starts from users.
I'm using mux gorilla
There is a PathPrefix function in mux. You can use it to make the path prefix i.e. "/user" in this case.
example:-
userHandler.go
func makeUserHandler()http.Handler{
//Define all you handlers here
r := mux.NewRouter()
r.HandleFunc("user/signup", signupHandler).Methods("POST")
r.HandleFunc("user/signin", signinHandler).Methods("POST")
r.HandleFunc("user/profile", profileHandler).Methods("GET")
r.HandleFunc("user/cart", cartHandler).Methods("POST")
r.HandleFunc("user/products", productsHandler).Methods("GET")
return r
}
main.go
r := mux.NewRouter()
r.PathPrefix("/user").Handler(makeUserHandler())
Please make necessary changes to the above code as per your requirement.

Iterate through all PostForm values Gin Gonic

Is there an easy way to list / iterate through all post values using Gin Gonic? (Go)
I have tried:
c.Request.ParseForm()
for key, value := range c.Request.PostForm {
log.Printf("POST %v = %v",key,value)
}
But this shows no values, however when I test the values directly from context:
log.Printf("POST email = %v", c.PostForm("email")
It outputs fine.
What I'm trying to do is to map all post values into a gin.H{} context, so that upon failure I can pass the posted values back into the .HTML template context and have them prefilled (along with my error message). Best I've found is manually wiring each POST value to the gin.H{} map, but for a large form these seems verbose and not ideal.
We also needed something like #BadPirate describes so if anyone need for gin 1.6.2
func register(c *gin.Context){
c.MultipartForm()
for key, value := range c.Request.PostForm {
log.Printf("%v = %v \n",key,value)
}
}
Thanks #BadPirate and #phoet for the info.
Issue here was the form (not shown) was a multipart form. ParseForm does not parse multipart forms, and thus, no data. The fix is to call ParseMultipartForm instead. Thanks to #phoet for pointing at the method in Gin Gonic for PostForm (which calls ParseMultipartForm for you, and does so automatically), which helped lead me to the answer.

golang mux, routing wildcard & custom func match

I'm using the mux package which seems to work quite well except that it doesn't seem to support complex routes or at least I don't get it how it does.
I have several routes as following:
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product)
router.HandleFunc("/{user}", userHome)
router.HandleFunc("/search/price", searchPage)
So I have two questions:
How can I define a wildcard route such /search/price/* so that a request such /search/price/29923/rage/200/color=red can match it ?
Is it possible to add custom conditions to an existing route ? e.g. if the route is /{productid}/{code} and function x returns true , use this handlerTrue, if it returns false use handlerFalse.
I've tried to add something like .MatcherFunc(myfunction(ip)bool) to the route but it complains that the router has no such method.
Currently I'm handling the 'custom' conditions inside the handler.
You can use regexps. Something like
router.HandleFunc(`/search/price/{rest:[a-zA-Z0-9=\-\/]+}`, searchPage)
That way, rest will just capture everything, so in your example rest would be 29923/rage/200/color=red. You will need to parse that in your code.
You probably want some like optional arguments, though.
router.HandleFunc(`/search{price:(\/price\/[0-9]+)?}{rage:(\/rage\/[0-9]+)?}{color:(\/color=[a-z]+)?}`, searchPage)
After that, you get vars price = "/price/29923", rage = "/rage/200" and color = "/color=red", that you still need to parse, but its easier, and you get to control which parameters are valid. It works as expected if you skip some parameter, eg. /search/price/29923/color=red will just give an empty rage variable, but still match.
I don't quite get your second question.
I'm not quite sure you need a "wildcard" route at all: you just need a route with multiple parameters:
/search/price/{price}/rage/{id}/color will work, noting that query strings don't need to be included in the matcher (you access those via request.URL.Query, whereas you access the mux variables via mux.Vars. You can also use regex to narrow down the accepted parameters.
It will also help to differentiate your user and product routes, perhaps by prefixing them with /user/{id} and /products/{id}/{code} (particularly for semantics).
As far as MatcherFunc goes, you need to make sure your function uses the same signature as MatcherFunc (which is a type): func MatchIPAddresses(*http.Request, *RouteMatch) bool would solve it. You can access the IP address via the Request struct by checking r.RemoteAddr or r.Header.Get("X-Forwarded-For") if you expect to be behind a proxy. I typically check both if one is empty ("").
i.e. (rough; you can clean this up a bit!)
func MatchIPAddresses(r *http.Request, rm *RouteMatch) bool {
if r.RemoteAddr == 8.8.8.8 {
return true
} else if r.Header.Get("X-Forwarded-For") == 8.8.8.8 {
return true
}
return false
}
To use a custom MatcherFunc with gorilla mux, you need to ensure that your matcher is actually of type mux.MatcherFunc. This is because MatcheFunc is not an interface type
// From mux/route.go line 303
// MatcherFunc is the function signature used by custom matchers.
type MatcherFunc func(*http.Request, *RouteMatch) bool
So you have to do something like:
var myMatcher mux.MatcherFunc = func(request *http.Request, match *mux.RouteMatch) bool {
// Your custom logic
return trueOrFalse
}
// You can then use it on your route like this.
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product).MatcherFunc(myMatcher)
With chi as router you can do the following:
Since the regex never matches a slash / you can simply match with *
e.g. for /search/price/29923/rage/200/color=red
router.Get(`/search/price/*`, priceHandler)
func DashboardFilesHandler(w http.ResponseWriter, r *http.Request) {
path := myhandler.UrlParam(r, "*")
// path will be '29923/rage/200/color=red'
}
See also: https://godoc.org/github.com/go-chi/chi
A placeholder with a name followed by a colon allows a regular
expression match, for example {number:\d+}. The regular expression
syntax is Go's normal regexp RE2 syntax, except that regular
expressions including { or } are not supported, and / will never be
matched. An anonymous regexp pattern is allowed, using an empty string
before the colon in the placeholder, such as {:\d+}
The special placeholder of asterisk matches the rest of the requested
URL. Any trailing characters in the pattern are ignored. This is the
only placeholder which will match / characters.

Resources