相信你对路由是干啥的已经有大致了解,具体来说就是让 Web 服务器根据规则,理解 HTTP 请求中的信息,匹配查找出对应的控制器,再将请求传递给控制器执行业务逻辑,简单来说就是制定匹配规则。
路由规则的需求回到我们的框架,开头我们说过希望使用者高效、易用地使用路由模块,那出于这一点考虑,基本需求可以有哪些呢?按照从简单到复杂排序,路由需求我整理成下面四点:
需求 1:HTTP 方法匹配早期的 WebService 比较简单,HTTP 请求体中的 Request Line 或许只会使用到 Request-URI 部分,但是随着 REST 风格 WebService 的流行,为了让 URI 更具可读性,在现在的路由输入中,HTTP Method 也是很重要的一部分了,所以,我们框架也需要支持多种 HTTP Method,比如 GET、POST、PUT、DELETE。
需求 2:静态路由匹配静态路由匹配是一个路由的基本功能,指的是路由规则中没有可变参数,即路由规则地址是固定的,与 Request-URI 完全匹配。我们在第一讲中提到的 DefaultServerMux 这个路由器,从内部的 map 中直接根据 key 寻找 value ,这种查找路由的方式就是静态路由匹配。
需求 3:批量通用前缀因为业务模块的划分,我们会同时为某个业务模块注册一批路由,所以在路由注册过程中,为了路由的可读性,一般习惯统一定义这批路由的通用前缀。比如 /user/info、/user/login 都是以 /user 开头,很方便使用者了解页面所属模块。所以如果路由有能力统一定义批量的通用前缀,那么在注册路由的过程中,会带来很大的便利。
需求 4:动态路由匹配这个需求是针对需求 2 改进的,因为 URL 中某个字段或者某些字段并不是固定的,是按照一定规则(比如是数字)变化的。那么,我们希望路由也能够支持这个规则,将这个动态变化的路由 URL 匹配出来。所以我们需要,使用自己定义的路由来补充,只支持静态匹配的 DefaultServerMux 默认路由。
// 注册路由规则
func registerRouter(core *framework.Core) {
// 需求1+2:HTTP方法+静态路由匹配
core.Post("/user/login", UserLoginController)
// 需求3:批量通用前缀
subjectApi := core.Group("/subject")
{
subjectApi.Post("/add", SubjectAddController)
// 需求4:动态路由
subjectApi.Delete("/:id", SubjectDelController)
subjectApi.Put("/:id", SubjectUpdateController)
subjectApi.Get("/:id", SubjectGetController)
subjectApi.Get("/list/all", SubjectListController)
}
}
按框架使用者使用路由的顺序分成四步来完善这个结构:定义路由 map、注册路由、匹配路由、填充 ServeHTTP 方法。
// 框架核心结构
type Core struct {
}
// 初始化框架核心结构
func NewCore() *Core {
return &Core{}
}
// 框架核心结构实现Handler接口
func (c *Core) ServeHTTP(response http.ResponseWriter, request *http.Request) {
// TODO
}
// 框架核心结构
type Core struct {
router map[string]map[string]ControllerHandler // 二级map
}
// 初始化框架核心结构
func NewCore() *Core {
// 定义二级map
getRouter := map[string]ControllerHandler{}
postRouter := map[string]ControllerHandler{}
putRouter := map[string]ControllerHandler{}
deleteRouter := map[string]ControllerHandler{}
// 将二级map写入一级map
router := map[string]map[string]ControllerHandler{}
router["GET"] = getRouter
router["POST"] = postRouter
router["PUT"] = putRouter
router["DELETE"] = deleteRouter
return &Core{router: router}
}
// 对应 Method = Get
func (c *Core) Get(url string, handler ControllerHandler) {
upperUrl := strings.ToUpper(url)
c.router["GET"][upperUrl] = handler
}
// 对应 Method = POST
func (c *Core) Post(url string, handler ControllerHandler) {
upperUrl := strings.ToUpper(url)
c.router["POST"][upperUrl] = handler
}
// 对应 Method = PUT
func (c *Core) Put(url string, handler ControllerHandler) {
upperUrl := strings.ToUpper(url)
c.router["PUT"][upperUrl] = handler
}
// 对应 Method = DELETE
func (c *Core) Delete(url string, handler ControllerHandler) {
upperUrl := strings.ToUpper(url)
c.router["DELETE"][upperUrl] = handler
}
实现批量通用前缀
// 注册路由规则
func registerRouter(core *framework.Core) {
// 需求1+2:HTTP方法+静态路由匹配
core.Get("/user/login", UserLoginController)
// 需求3:批量通用前缀
subjectApi := core.Group("/subject")
{
subjectApi.Get("/list", SubjectListController)
}
}