不看就落后了,Go 1.22 中更好的http router

简介: 不看就落后了,Go 1.22 中更好的http router

很多人为了使用 Go web 中更好的路由,会使用第三方的库 httproutergorilla/mux等。在明年的春节左右发布的 Go 1.22 中,Go 官方终于对标准库中的http.ServeMux下手了,对它的功能进行了优化,终于可以抛弃第三方库了。


一个令人兴奋的提案预计将在 Go 1.22 中实现—— 增强标准库net/http包中默认 HTTP 服务多路复用器的模式匹配能力。


现有的多路复用器(http.ServeMux)提供了基本的路径匹配,但除此之外没有太多。这导致了一堆的第三方库实现了更强大的功能。


Go 1.22 中的新多路复用器将通过提供高级匹配能力来显著弥合与第三方库的差距。在这篇短文中,我将快速介绍新的多路复用器(mux)。我还将给出 REST 服务器的示例,并比较新的标准库muxgorilla/mux的性能。


使用新的 mux


如果你曾经在 Go 中使用过第三方 mux/router(比如gorilla/mux),那么使用新的标准 mux 将是简单而熟悉的。从阅读它的文档开始——它简短丝滑。


让我们来看几个基本用法示例。我们的第一个示例演示了 mux 的一些新模式匹配功能:


package main
import (
  "fmt"
  "net/http"
)
func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("GET /path/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "got path\n")
  })
  mux.HandleFunc("/task/{id}/", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "handling task with id=%v\n", id)
  })
  http.ListenAndServe("localhost:8090", mux)
}


经验丰富的 Go 程序员会立即注意到两个新功能:


  • 在第一个处理程序中,HTTP method(在本例中为GET)被明确指定为模式的一部分。这意味着该处理程序将只触发以/path/开头的路径的GET请求,而不触发其他 HTTP method。
  • 在第二个处理程序中,第二个路径组件 - {id}中有一个通配符,这是以前不支持的。通配符将匹配单个路径组件,然后处理程序可以通过请求的PathValue方法访问匹配的值。


由于 Go 1.22 尚未发布,你可以使用gotip运行此示例。请参阅完整的代码示例以及运行此程序的完整说明。让我们来试试这个服务器:


$ gotip run sample.go


在一个单独的终端中,我们可以发出一些 curl 调用来测试它:


$ curl localhost:8090/what/
404 page not found
$ curl localhost:8090/path/
got path
$ curl -X POST localhost:8090/path/
Method Not Allowed
$ curl localhost:8090/task/f0cd2e/
handling task with id=f0cd2e


请注意,服务器如何拒绝对/path/的 POST 请求,同时允许(curl 的默认值)GET请求。还要注意,当请求匹配时,id通配符是如何被分配一个值的。我再次鼓励您查看新ServeMux的文档。您将了解其他功能,如将尾随路径与带有{id}的通配符匹配,路径以{$}结尾的严格匹配以及其他规则。


提案中特别注意不同模式之间的潜在冲突。请考虑此设置:


mux := http.NewServeMux()
mux.HandleFunc("/task/{id}/status/", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        fmt.Fprintf(w, "handling task status with id=%v\n", id)
})
mux.HandleFunc("/task/0/{action}/", func(w http.ResponseWriter, r *http.Request) {
        action := r.PathValue("action")
        fmt.Fprintf(w, "handling task 0 with action=%v\n", action)
})


假设服务器收到/task/0/status/的请求——它应该转到哪个处理程序?两者都匹配!因此,新的ServeMux文档仔细地描述了模式的优先级规则以及潜在的冲突。如果发生冲突,注册会 panic。事实上,对于上面的例子,我们得到了如下内容:


panic: pattern "/task/0/{action}/" (registered at sample-conflict.go:14) conflicts with pattern "/task/{id}/status/" (registered at sample-conflict.go:10):
/task/0/{action}/ and /task/{id}/status/ both match some paths, like "/task/0/status/".
But neither is more specific than the other.
/task/0/{action}/ matches "/task/0/action/", but /task/{id}/status/ doesn't.
/task/{id}/status/ matches "/task/id/status/", but /task/0/{action}/ doesn't.


该信息详细且有用。如果我们在复杂的注册方案中遇到冲突(尤其是当模式在源代码中的多个位置注册时),这些细节会非常有用。


用新的 mux 实现服务器


REST Servers in Go series的 REST 服务器使用几种不同的方法为 Go 中的任务/待办事项列表应用程序实现了一个简单的服务器。第 1 部分从标准库开始,第 2 部分使用gorilla/mux路由器重新实现了相同的服务器。


现在是再次实现它的好时机,但有了 Go 1.22 的增强mux;将该解决方案与使用gorilla/mux的解决方案进行比较将特别有趣。


此项目的完整代码可在此处获得。让我们看看几个有代表性的代码示例,从模式注册开始:

mux := http.NewServeMux()
server := NewTaskServer()
mux.HandleFunc("POST /task/", server.createTaskHandler)
mux.HandleFunc("GET /task/", server.getAllTasksHandler)
mux.HandleFunc("DELETE /task/", server.deleteAllTasksHandler)
mux.HandleFunc("GET /task/{id}/", server.getTaskHandler)
mux.HandleFunc("DELETE /task/{id}/", server.deleteTaskHandler)
mux.HandleFunc("GET /tag/{tag}/", server.tagHandler)
mux.HandleFunc("GET /due/{year}/{month}/{day}/", server.dueHandler)


就像在gorilla/mux示例中一样,这里我们使用特定的 HTTP method 将请求(具有相同路径)路由到不同的处理程序;使用旧的http.ServeMux这样的匹配器就必须转到同一个处理程序,然后由该处理程序根据该方法决定要做什么。


让我们看看其中一个处理程序:


func (ts *taskServer) getTaskHandler(w http.ResponseWriter, req *http.Request) {
  log.Printf("handling get task at %s\n", req.URL.Path)
  id, err := strconv.Atoi(req.PathValue("id"))
  if err != nil {
    http.Error(w, "invalid id", http.StatusBadRequest)
    return
  }
  task, err := ts.store.GetTask(id)
  if err != nil {
    http.Error(w, err.Error(), http.StatusNotFound)
    return
  }
  renderJSON(w, task)
}


它从req.PathValue("id")中提取 ID 值。类似于 Gorilla 方法;然而,由于我们没有正则表达式指定{id}只匹配整数,因此我们必须注意strconv.Atoi返回的错误。


总之,最终结果与第 2 部分中使用gorilla/mux的解决方案非常相似。与普通的标准库方法相比,处理程序的方式要好得多,因为 mux 现在可以进行更复杂的路由,而不会将许多路由决策留给处理程序本身。


结论


“我应该使用哪个 router 库?”一直是 Go 初学者的常见问题。我相信在 Go 1.22 发布后,这个问题的常见答案会发生变化,因为许多人会发现新的标准库 mux 足以满足他们的需求,而无需求助于第三方软件包。


其他人会坚持使用熟悉的第三方库,这完全没关系。像gorilla/mux这样的路由器仍然提供比标准库更多的功能;除此之外,许多 Go 程序员选择了像 Gin 这样的轻量级框架,它提供了一个路由器,但也提供了用于构建 web 后端的额外工具。


总而言之,这对所有 Go 用户来说无疑是一个积极的变化。无论人们是使用第三方软件包还是坚持使用标准库,让标准库更有能力对整个社区来说都是一个积极的方面。


翻译自Better HTTP server routing in Go 1.22。


相关文章
|
6月前
|
中间件 Go 开发者
Go net http包
Go net http包
60 0
|
3月前
|
JSON 安全 前端开发
类型安全的 Go HTTP 请求
类型安全的 Go HTTP 请求
|
14天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
43 13
|
3月前
|
数据采集 缓存 IDE
Go中遇到http code 206和302的获取数据的解决方案
文章提供了解决Go语言中处理HTTP状态码206(部分内容)和302(重定向)的方案,包括如何获取部分数据和真实请求地址的方法,以便程序员能快速完成工作,享受七夕时光。
166 0
Go中遇到http code 206和302的获取数据的解决方案
|
3月前
|
JSON 测试技术 Go
Go Kit中读取原始HTTP请求体的方法
Go Kit中读取原始HTTP请求体的方法
|
3月前
|
网络协议 Go
go的net/http有哪些值得关注的细节?
go的net/http有哪些值得关注的细节?
|
3月前
|
网络协议 Go
【go笔记】简单的http服务
【go笔记】简单的http服务
|
4月前
|
JSON Java Serverless
函数计算产品使用问题之如何使用Go SDK从HTTP上下文中提取JSON数据
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
4月前
|
Go
Go中 net/http 使用
Go中 net/http 使用
27 0
|
4月前
|
网络协议 程序员 应用服务中间件
Swoole与Go系列教程之HTTP服务的应用
PHP 曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole 曾经尝试填补空白,但局限性也比较的明显。Go 语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转型。
1004 0
Swoole与Go系列教程之HTTP服务的应用