• Golang【Web 入门】 07 路由 - http.ServeMux


    说明

    goblog 需要一款灵活的路由器来搭配 MVC 程序结构。

    Go Web 开发有各式各样的路由器可供选择,我们先来看下 Go 标准库 net/http 包里的 http.ServeMux。

    ServeMux 和 Handler

    Go 语言中处理 HTTP 请求主要跟两个东西相关:ServeMux 和 Handler。

    ServeMux 本质上是一个 HTTP 请求路由器(或者叫多路复用器,Multiplexor)。它把收到的请求与一组预先定义的 URL 路径列表做对比,然后在匹配到路径的时候调用关联的处理器(Handler)。

    http 的 ServeMux 虽听起来陌生,事实上我们已经在使用它了。

    重构:区分不同的 Handler

    先来重构下我们的代码,修改如下:

    main.go

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func defaultHandler(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "text/html; charset=utf-8")
    	if r.URL.Path == "/" {
    		fmt.Fprint(w, "

    Hello, 欢迎来到 goblog!

    "
    ) } else { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, "

    请求页面未找到 :(

    "
    + "

    如有疑惑,请联系我们。

    "
    ) } } func aboutHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "此博客是用以记录编程笔记,如您有反馈或建议,请联系 "+ "summer@example.com") } func main() { http.HandleFunc("/", defaultHandler) http.HandleFunc("/about", aboutHandler) http.ListenAndServe(":3000", nil) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    浏览器访问以下三个链接,发现与之前一致:

    • localhost:3000/
    • localhost:3000/about
    • localhost:3000/no-where

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    查看 http.HandleFunc 源码

    在 VSCode 编辑器里,把鼠标放置在 main 方法中的 http.HandleFunc 上,并同时按住 Ctrl 键(不要松开),当出现下划线的时候,点击进去:

    在这里插入图片描述
    此时我们看到的代码,是 net/http 包的源码。
    显而易见,http 包也是由 Go 代码实现的。

    仔细看下 HandleFunc 函数的定义:

    func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
        DefaultServeMux.HandleFunc(pattern, handler)
    }
    
    • 1
    • 2
    • 3

    参数:

    • pattern 是 URI 的规则,例如 / 或者 about。
    • handler 是供调用的函数。

    http.HandleFunc() 函数是对 DefaultServeMux.HandleFunc() 的封装,而我们在用相同的方法查看 http.ListenAndServe() 的源码:

    在这里插入图片描述
    注释里有这么一段:

    The handler is typically nil, in which case the DefaultServeMux is used。

    handler 通常为 nil,此种情况下会使用 DefaultServeMux。

    重构:使用自定义的 ServeMux

    main.go

    .
    .
    .
    
    func main() {
        router := http.NewServeMux()
    
        router.HandleFunc("/", defaultHandler)
        router.HandleFunc("/about", aboutHandler)
    
        http.ListenAndServe(":3000", router)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    浏览器访问以下三个链接,与之前一致:

    • localhost:3000/
    • localhost:3000/about
    • localhost:3000/no-where

    http.ServeMux 的局限性

    http.ServeMux 在 goblog 中使用,会遇到以下几个问题:

    • 不支持 URI 路径参数
    • 不支持请求方法过滤
    • 不支持路由命名

    URI 路径参数

    例如说博客详情页,我们的 URI 是 articles/1 这样来查看 ID 为 1 的文章。

    http.ServeMux 也可以实现,新增一个路由作为示范:

    main.go

    func main() {
        router := http.NewServeMux()
    
        router.HandleFunc("/", defaultHandler)
        router.HandleFunc("/about", aboutHandler)
    
        // 文章详情
        router.HandleFunc("/articles/", func(w http.ResponseWriter, r *http.Request) {
            id := strings.SplitN(r.URL.Path, "/", 3)[2]
            fmt.Fprint(w, "文章 ID:"+id)
        })
    
        http.ListenAndServe(":3000", router)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    不够直观,且徒增了代码的维护成本。

    请求方法过滤

    无法直接从路由上区分 POST 或者 GET 等 HTTP 请求方法,只能手动判断,例如:

    main.go

    func main() {
    	router := http.NewServeMux()
    
    	router.HandleFunc("/", defaultHandler)
    	router.HandleFunc("/about", aboutHandler)
    
    	// 列表 or 创建
    	router.HandleFunc("/articles", func(w http.ResponseWriter, r *http.Request) {
    		switch r.Method {
    		case "GET":
    			fmt.Fprint(w, "访问文章列表")
    		case "POST":
    			fmt.Fprint(w, "创建新的文章")
    		}
    	})
    
    	http.ListenAndServe(":3000", router)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述
    我们使用 CURL 来测试下:

    E:\golang\src>curl -X POST http://127.0.0.1:3000/articles
    创建新的文章
    E:\golang\src>
    
    E:\golang\src>curl http://127.0.0.1:3000/articles
    访问文章列表
    E:\golang\src>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以实现,但是要多出来很多代码。

    不支持路由命名

    路由命名是一套允许我们快速修改页面里显示 URL 的机制。

    例如说我们的文章详情页,URL 是:

    http://example.com/articles/{id}
    
    • 1

    项目随着时间的推移,变得非常巨大,在几十个页面里都存在这个 URI 。突然有个需求或者有其他不可控因素,要求我们修改链接为:

    http://example.com/posts/{id}
    
    • 1

    那么我们只能到这个几十个页面里去修改。

    使用路由命名的话,我们为 articles/{id} 这个路由命名为articles.show,几十个页面在编码时,都使用这个路由名称而不是具体的 URI,遇到修改的需求时,我们只需在定义路由这一个地方修改即可。

    目前 http.ServeMux 不支持此功能。

    http.ServeMux 的优缺点

    如上所述,Go 开源社区里有诸多路由器可供选择,那么标准库的 http.ServeMux 对比这些路由器有什么优缺点呢?

    优点

    • 标准库意味着随着 Go 打包安装,无需另行安装
    • 测试充分
    • 稳定、兼容性强
    • 简单,高效

    缺点

    • 缺少 Web 开发常见的特性
    • 在复杂的项目中使用,需要你写更多的代码

    开发效率和运行效率,永远是对立面。

    Go 因为其诞生的背景(Google 的大流量),以及核心成员的出身(底层语言和系统的缔造者),Go 标准库选择 运行效率 高于 开发效率,所以对一些常见的功能并没有添加到标准库中,这是情有可原的。

    然而在 goblog 中,我们将会有数据库等与第三方服务的请求操作,跟这类编码比起来,路由解析这点性能优化微不足道。所以在这个项目中,我们将在性能不会牺牲太大的情况下,选择 开发效率 多一点。

    标准库里的就是最好的?

    新手常常会认为标准库里的就是最好的。
    其实不然,标准库也是由 Go 语言编写的。

    就拿 net/http 来讲,GitHub 上有一个项目专门对 Go 中知名的 HTTP 路由器性能做对比,结果是第三方包 HttpRouter 比 http.ServeMux 还要快不少。

    事实上,标准库最大的优点是 Go 自带。

    所以不止在选择 HTTP 服务器,在选择其他解决方案时,都可以大胆的使用一些 Go 开源社区优秀的第三方包。

  • 相关阅读:
    LabVIEW利用人工神经网络辅助进行结冰检测
    spring mvc源码分析之请求分发
    windows工作窗口太多,需要频繁切换?摸鱼利器你必须了解下
    C++DAY43
    springboot+网络空间安全实验教学中心门户网站 毕业设计-附源码191220
    Linux搭建Zookeeper伪集群详解
    kafka基础知识点整理
    数组能开空间大小
    jQuery层次选择器
    VueX插件、持久化存储、分包module模块vuex
  • 原文地址:https://blog.csdn.net/weiguang102/article/details/128151141