相信你对路由是干啥的已经有大致了解,具体来说就是让 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 默认路由。

    1. // 注册路由规则
    2. func registerRouter(core *framework.Core) {
    3. // 需求1+2:HTTP方法+静态路由匹配
    4. core.Post("/user/login", UserLoginController)
    5. // 需求3:批量通用前缀
    6. subjectApi := core.Group("/subject")
    7. {
    8. subjectApi.Post("/add", SubjectAddController)
    9. // 需求4:动态路由
    10. subjectApi.Delete("/:id", SubjectDelController)
    11. subjectApi.Put("/:id", SubjectUpdateController)
    12. subjectApi.Get("/:id", SubjectGetController)
    13. subjectApi.Get("/list/all", SubjectListController)
    14. }
    15. }

    按框架使用者使用路由的顺序分成四步来完善这个结构:定义路由 map、注册路由、匹配路由、填充 ServeHTTP 方法。

    1. // 框架核心结构
    2. type Core struct {
    3. }
    4. // 初始化框架核心结构
    5. func NewCore() *Core {
    6. return &Core{}
    7. }
    8. // 框架核心结构实现Handler接口
    9. func (c *Core) ServeHTTP(response http.ResponseWriter, request *http.Request) {
    10. // TODO
    11. }
    1. // 框架核心结构
    2. type Core struct {
    3. router map[string]map[string]ControllerHandler // 二级map
    4. }
    5. // 初始化框架核心结构
    6. func NewCore() *Core {
    7. // 定义二级map
    8. getRouter := map[string]ControllerHandler{}
    9. postRouter := map[string]ControllerHandler{}
    10. putRouter := map[string]ControllerHandler{}
    11. deleteRouter := map[string]ControllerHandler{}
    12. // 将二级map写入一级map
    13. router := map[string]map[string]ControllerHandler{}
    14. router["GET"] = getRouter
    15. router["POST"] = postRouter
    16. router["PUT"] = putRouter
    17. router["DELETE"] = deleteRouter
    18. return &Core{router: router}
    19. }
    1. // 对应 Method = Get
    2. func (c *Core) Get(url string, handler ControllerHandler) {
    3. upperUrl := strings.ToUpper(url)
    4. c.router["GET"][upperUrl] = handler
    5. }
    6. // 对应 Method = POST
    7. func (c *Core) Post(url string, handler ControllerHandler) {
    8. upperUrl := strings.ToUpper(url)
    9. c.router["POST"][upperUrl] = handler
    10. }
    11. // 对应 Method = PUT
    12. func (c *Core) Put(url string, handler ControllerHandler) {
    13. upperUrl := strings.ToUpper(url)
    14. c.router["PUT"][upperUrl] = handler
    15. }
    16. // 对应 Method = DELETE
    17. func (c *Core) Delete(url string, handler ControllerHandler) {
    18. upperUrl := strings.ToUpper(url)
    19. c.router["DELETE"][upperUrl] = handler
    20. }

    实现批量通用前缀

    1. // 注册路由规则
    2. func registerRouter(core *framework.Core) {
    3. // 需求1+2:HTTP方法+静态路由匹配
    4. core.Get("/user/login", UserLoginController)
    5. // 需求3:批量通用前缀
    6. subjectApi := core.Group("/subject")
    7. {
    8. subjectApi.Get("/list", SubjectListController)
    9. }
    10. }