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.
Related
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()
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
}
})
I understand how to use multiple return values in go. I further understand that in most cases one of the returns is an error, so ignoring returned values can be dangerous.
Is there a way to ignore a value in struct initializer like this? The below example does not work as Split returns two values, but I am interested only in the first one. I can of course create a variable but...
someFile := "test/filename.ext"
contrivedStruct := []struct{
parentDir string
}{
{ parentDir: filepath.Split(someFile) },
}
It's not possible to use only one of the return values when initializing members in Go.
Using variables clearly expresses your intent.
Go sometimes feels like it could be more succinct, but the Go authors favoured readability over brevity.
Alternatively, use a wrapper function. There are several 'Must' wrapper functions in the standard library, like: template.Must.
func first(args ...string) string {
return args[0]
}
For your particular example, splitting paths, see filepath.Base or filepath.Dir.
No, there is no way to skip one of the returned values in structure initializer.
I have two golang functions, doing exactly same thing, one takes input a slice and the other takes a map as input. I want to combine this into one function, to avoid duplication.
func DoSomething([]data) {
//do something.
}
func DoSomething(map[string]data) {
//do something.
}
Combined function may look like:
func DoSomethingNew (param type) {
//param could be map or slice
}
I want to know if it is possible to pass different types to same function in golang and how. I googled but couldn't find anything relevant.
You can use interfaces Go Playground
func F(data interface{}) {
switch data.(type){
case []int:
fmt.Println("Slice")
fmt.Println(data)
case map[string]int:
fmt.Println("Map")
fmt.Println(data)
}
fmt.Println()
}
where you actually check for the type and do something based on the type.
There are several ways you could do this, but the simple way would be to make it so DoSomethingNew accepts the interface{} type. Inside of the method you would then do a type switch or in this case with only two options, perhaps just one type assertion, followed by the other, returning error if both fail. Another option would be to have both as arguments and check for nil inside the function with a similar if, else-if, else pattern to handle the error if the input is of neither types you're looking for. To make your code more safe you could move to a more strict interface than the empty one which all types implement. You could also do some method chaining or even implement the method with using the types themselves as the receiving type. Here's an example that shows a few of the ideas; https://play.golang.org/p/_v2AyFjGzv
I am writing what is essentially a chat program. I want to include some special irc style commands in it and I just can't seem to conceptualize how to structure the program. I'm wondering if people could help me out from a high level perspective. I'm not looking for code, just ideas on how to best proceed.
My best attempt is to have a Command struct like below:
type Command struct {
name string // the name of the command
function string // the function that will be called upon match
help string // the help message for the command
regex string // the regex pattern that will run the command
}
and then have a slice of Commands and just iterate over that every time I receive a message from the client. If the received data matches the regex then have the reflect (I think this will work) package call "function". For some reason, I feel like there has to be a better way. I'm learning to code Go on my own and don't have resources available to me to bounce ideas off of. I would very much appreciate your thoughts on this.
In IRC style commands usually a command line looks like this:
/cmd [param1] [param2] ... [paramn]
When such a command is received, you can split it using strings.Split() to get the parts or tokens of the command. By this you will have the first token identifying the command.
You can build a map[string]Command map where you map from text commands to their Command structure. In this map you can get commands by simply indexing the map, e.g.:
cmdMap := make(map[string]Command)
// Populate map
textCmd := "/help"
cmd := cmdMap[textCmd]
If you want to have command aliases (e.g. you want /help and /h and /? all to do the same), you can store the list of aliases for each command and when you build the cmdMap, also add entries for all aliases to point to the same Command structure, in which case you should define it like this:
cmdMap := make(map[string]*Command)
helpCmd := &Command{...} // Create help command
cmdMap["/help"] = helpCmd
cmdMap["/h"] = helpCmd
cmdMap["/?"] = helpCmd
Note: you could also strip off the leading slash '/' and just use the rest of the command ("help", "h" and "?" in this case) to init your map, it's up to you.
Also you don't have to store the name of the function, functions in Go are values so you can have a function field in your Command struct and then you can call that function without reflection. For example:
func DoSomething() {
fmt.Println("Doing something...")
}
var someFv = DoSomething
// And now you can do:
someFv()
See Function types and Function literals in the Go Language Specification.
First off, you don't need to use reflect. You can have the Command struct contain a member with a func type.
type Command struct {
name string // the name of the command
f func(string) // the function that will be called upon match
help string // the help message for the command
regex regexp.Regexp // the regex pattern that will run the command
}
func processMessage(text string){
for _,cmd := range(allCmds){
if cmd.regex.MatchString(text){
cmd.f(text)
return
}
}
defaultAction(text) //or just add a catch-all with a regex of `.*`
}
Then you can add commands with a function of the appropriate signature:
cmd := Command{name: "foo",f: func(text string){fmt.Println(text)}}
it doesn't have to have exactly that signature. You can have it accept a connection or whatever. You also don't have to inline the function definition, you can reference any function you want that has the appropriate signature.