The GoFrame
framework has built-in powerful routing capabilities and offers superior routing features compared to similar frameworks. It supports popular named matching rules, fuzzy matching rules, and field matching rules, along with an excellent priority management mechanism.
An Example
Before diving into the core content of this chapter, let's look at a simple example of using dynamic routing:
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
s.BindHandler("/:name", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/update", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/:action", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.SetPort(8199)
s.Run()
}
The example above demonstrates the three types of fuzzy matching routing rules supported by the goframe
framework: :name
, *any
, {field}
, representing named matching rules, fuzzy matching rules, and field matching rules respectively. Different rules use the /
symbol to separate levels, and the routing lookup uses a depth-first algorithm, where rules with deeper levels have higher priority. Let's run the example and see the effect by accessing some URLs:
URL Result
http://127.0.0.1:8199/user/list/2.html /user/list/{field}.html
http://127.0.0.1:8199/user/update /:name/update
http://127.0.0.1:8199/user/info /:name/:action
http://127.0.0.1:8199/user /:name/*any
In this example, we can also see that due to priority restrictions, the route rule /:name
will be overridden by the /:name/*any
rule, so it will not be matched. Therefore, when assigning route rules, unified planning and management are needed to avoid such situations.
Registration Rules
Route Registration Parameters
The most basic method for binding routes is the BindHandler
method. Let's take a look at the prototype of the BindHandler
method we've been using:
func (s *Server) BindHandler(pattern string, handler interface{})
pattern
Parameter
The pattern
is a route registration rule string, used in other route registration methods as well. The parameter format is as follows:
[HTTPMethod:]RoutePattern[@Domain]
In which HTTPMethod
(GET/PUT/POST/DELETE/PATCH/HEAD/CONNECT/OPTIONS/TRACE
) and @Domain
are optional parameters. In most scenarios, you can directly provide the route rule parameter, and BindHandler
will automatically bind all request methods. If HTTPMethod
is specified, the route rule will only be valid for that request method. By specifying @Domain
, the rule will only be valid under that domain.
BindHandler
is the most native method for registering routes. In most scenarios, we usually use group routing to manage reasons, which will be introduced in later chapters: Registration - Group Routing.
handler
Parameter
The handler
parameter is usually used to specify the route function. Our most basic examples use a function to register routes; a route function needs to meet the following definition, meaning it only needs to be able to receive the request object ghttp.Request
:
func(r *ghttp.Request) {
// ...
}
Let's look at an example:
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
// This route rule will only be valid under GET requests
s.BindHandler("GET:/{table}/list/{page}.html", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// This route rule will only be valid under GET requests and the localhost domain
s.BindHandler("GET:/order/info/{order_id}@localhost", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// This route rule will only be valid under DELETE requests
s.BindHandler("DELETE:/comment/{id}", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
s.SetPort(8199)
s.Run()
}
The returned parameter r.Router
provides information on the current matched route rule. When accessing this method, the server will output information on the current matched route rule. After execution, we can test using the curl
command in the terminal:
$ curl -XGET http://127.0.0.1:8199/order/list/1.html
{"Domain":"default","Method":"GET","Priority":3,"Uri":"/{table}/list/{page}.html"}
$ curl -XGET http://127.0.0.1:8199/order/info/1
Not Found
$ curl -XGET http://localhost:8199/order/info/1
{"Domain":"localhost","Method":"GET","Priority":3,"Uri":"/order/info/{order_id}"}
$ curl -XDELETE http://127.0.0.1:8199/comment/1000
{"Domain":"default","Method":"DELETE","Priority":2,"Uri":"/comment/{id}"}
$ curl -XGET http://127.0.0.1:8199/comment/1000
Not Found
Exact Matching Rules
Exact matching rules are the rules without any dynamic rules, such as user
, order
, info
, etc., which are rules with a determined name. In most scenarios, exact matching rules are used together with dynamic rules for route registration (e.g., /:name/list
, where level 1 :name
is a named matching rule, and level 2 list
is an exact matching rule).
Dynamic Routing Rules
Dynamic routing rules are divided into three types: named matching rules, fuzzy matching rules, and field matching rules. The underlying data structure of dynamic routing is a routing tree
constructed by hash tables
by levels and double-linked lists
. The hash tables assist in efficiently matching the levels of the URI
, and the data linked list is used for priority control; rules of the same level are sorted by priority, with higher-priority rules placed at the head of the list. The underlying routing rule matching calculation with the request URI
uses regular expressions and optimally utilizes caching mechanisms for highly efficient execution.
All matched parameters will be passed to the business layer as Router
parameters, which can be retrieved using the following method of the ghttp.Request
object:
func (r *Request) GetRouter(key string, def ...interface{}) *gvar.Var
You can also use the ghttp.Request.Get
method to retrieve matched routing parameters.
Named Matching Rules
Uses the :name
method for matching (name
is a custom match name) and performs a named match for parameters at a specified level of the URI
(similar to regular ([^/]+)
, this URI
level must have a value). The corresponding matched parameters will be parsed into Router
parameters and passed to the registered service API for use.
Matching example 1:
rule: /user/:user
/user/john match
/user/you match
/user/john/profile no match
/user/ no match
Matching example 2:
rule: /:name/action
/john/name no match
/john/action match
/smith/info no match
/smith/info/age no match
/smith/action match
Matching example 3:
rule: /:name/:action
/john/name match
/john/info match
/smith/info match
/smith/info/age no match
/smith/action/del no match
Fuzzy Matching Rules
Uses the *any
method for matching (any
is a custom match name) for fuzzy matching of parameters after the specified position of the URI
(similar to regular (.*)
, this URI
level can be empty), and parses matched parameters into Router
parameters to be passed to the registered service API for use.
Matching example 1:
rule: /src/*path
/src/ match
/src/somefile.go match
/src/subdir/somefile.go match
/user/ no match
/user/john no match
Matching example 2:
rule: /src/*path/:action
/src/ no match
/src/somefile.go match
/src/somefile.go/del match
/src/subdir/file.go/del match
Matching example 3:
rule: /src/*path/show
/src/ no match
/src/somefile.go no match
/src/somefile.go/del no match
/src/somefile.go/show match
/src/subdir/file.go/show match
/src/show match
Field Matching Rules
Uses the {field}
method for matching (field
is a custom match name), which can perform segment matches of parameters at any position in the URI
(similar to regular ([\w\.\-]+)
, this URI
level must have a value and multiple field matches can occur at the same level), and parses matched parameters into Router
parameters to be passed to the registered service API for use.
Matching example 1:
rule: /order/list/{page}.php
/order/list/1.php match
/order/list/666.php match
/order/list/2.php5 no match
/order/list/1 no match
/order/list no match
Matching example 2:
rule: /db-{table}/{id}
/db-user/1 match
/db-user/2 match
/db/user/1 no match
/db-order/100 match
/database-order/100 no match
Matching example 3:
rule: /{obj}-{act}/*param
/user-delete/10 match
/order-update/20 match
/log-list match
/log/list/1 no match
/comment/delete/10 no match
Dynamic Route Example
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
// A simple paging route example
s.BindHandler("/user/list/{page}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Get("page"))
})
// Mixing {xxx} rule with :xxx rule
s.BindHandler("/{object}/:attr/{act}.php", func(r *ghttp.Request){
r.Response.Writeln(r.Get("object"))
r.Response.Writeln(r.Get("attr"))
r.Response.Writeln(r.Get("act"))
})
// Mixing multiple fuzzy matching rules
s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request){
r.Response.Writeln(r.Get("class"))
r.Response.Writeln(r.Get("course"))
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("act"))
})
s.SetPort(8199)
s.Run()
}
After execution, we can test it using the curl
command or a browser access method. Below are the test results:
$ curl -XGET http://127.0.0.1:8199/user/list/1.html
1
$ curl -XGET http://127.0.0.1:8199/user/info/save.php
user
info
save
$ curl -XGET http://127.0.0.1:8199/class3-math/john/score
class3
math
john
score
Priority Control
Priority control follows a depth-first strategy. Here's a brief strategy calculation:
- Rules with deeper levels have higher priority;
- At the same level, exact matching has higher priority than fuzzy matching;
- Within the same level, fuzzy matching priority is: field matching > named matching > fuzzy matching;
Let's see examples (the rule on the left has a higher priority than the one on the right):
/:name > /*any
/user/name > /user/:action
/:name/info > /:name/:action
/:name/:action > /:name/*action
/:name/{action} > /:name/:action
/src/path/del > /src/path
/src/path/del > /src/path/:action
/src/path/*any > /src/path