Skip to main content
Version: 2.8.x(Latest)

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.

tip

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:

  1. Rules with deeper levels have higher priority;
  2. At the same level, exact matching has higher priority than fuzzy matching;
  3. 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