httprouter and negroni for public and private routing middleware - go

I'm having a difficult time understanding how to use negroni and httprouter together.
I have a couple of public routes, such as /api/v1/ping
I have a bunch of private routes that need auth middleware, such as /api/v1/user
If I want negroni Common middleware for all of my routes, but I want to apply the auth middleware and others to only the private routes, how can I set this up?
v1.router := httprouter.New()
v1.router.GET("/api/v1/ping", v1.ping)
v1.router.GET("/api/v1/user", v1.getUsers)
n := negroni.Classic()
n.UseHandler(v1.router)
http.ListenAndServe(port, n)

You could try and adapt the technique described in "Path Prefix Middleware in Go ", which is using net/http/#ServeMux, with another router (gorilla/mux), but should be valid for julienschmidt/httprouter as well:
Specifying middleware based on route prefixes
This is where the magic happens, and it is also where things get confusing.
The easiesy way I have found to specify middleware for a path prefix is to setup a second muxer (we use the sirMuxalot variable for ours below) that has the path prefixes we want to apply middleware to, and to then pass in our original router wrapped in some middleware for those routes.
This works because the sirMuxalot router won’t ever call the middleware-wrapped router unless the path prefix we define gets matched with the incoming web request’s path.
sirMuxalot := http.NewServeMux()
sirMuxalot.Handle("/", r)
sirMuxalot.Handle("/api/", negroni.New(
negroni.HandlerFunc(APIMiddleware),
negroni.Wrap(r),
))
sirMuxalot.Handle("/dashboard/", negroni.New(
negroni.HandlerFunc(DashboardMiddleware),
negroni.Wrap(r),
))
n := negroni.Classic()
n.UseHandler(sirMuxalot)
http.ListenAndServe(":3000", n)

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()

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.

How to use multiple parameters in query router

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
}
})

How to add new route to autogenerate resources?

please help me, I want add new route/func to generate resource (etc. users).
app.Resource("/users", UsersResource{})
I found func addRoute, but doesn't work.
func (a *App) addRoute(method string, url string, h Handler) *RouteInfo { ...
My idea is something like this.
"/users/handleSomething/idOfUser"
Right now i have only this func:
List(Context)
Show(Context)
New(Context)
Create(Context)
Edit(Context)
Update(Context)
Destroy(Context)
Thx for your time and help :)
PS: Sorry for my English :(
If you want additional routes you can use the methods GET,POST,PUT, ... that are defined on the *App type. (https://gobuffalo.io/en/docs/routing#supported-http-methods)
To handle dynamic path segments in the route you can use named parameters. (https://gobuffalo.io/en/docs/routing#named-parameters)
Example:
app.PATCH("/users/handleSomething/{id}", handleSomethingHandler)
To retrieve the value of the named parameter you can use c.Param("id") where c is the buffalo context passed in to your handler.

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