chi
是用于构建GO HTTP服务的轻巧,惯用和可组合的路由器。它尤其擅长帮助您编写大型REST API服务,随着项目的增长和变化,该服务可保持可维护。 chi
建立在GO 1.7中引入的新context
软件包上,以处理处理程序链上的信号,取消和请求划分的值。
该项目的重点是寻找一个优雅而舒适的设计,用于编写REST API服务器,该设计在开发Pressly API服务期间为我们的公共API服务提供动力,这反过来又为我们所有客户端应用程序提供了支持。
Chi设计的主要考虑因素是:项目结构,可维护性,标准HTTP处理程序(仅stdlib),开发人员的生产率以及将大型系统解构为许多小部分。核心路由器github.com/go-chi/chi
很小(少于1000个LOC),但我们还提供了一些有用的/可选的子包:中间件,渲染和DOCGEN。我们希望您也喜欢它!
go get -u github.com/go-chi/chi/v5
net/http
兼容的任何HTTP或中间件PKGcontext
软件包上,提供价值链,取消和超时docgen
自动生成从源到JSON或MARKDOWN的路由文档有关各种示例,请参见_ examples/。
尽可能容易:
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main () {
r := chi . NewRouter ()
r . Use ( middleware . Logger )
r . Get ( "/" , func ( w http. ResponseWriter , r * http. Request ) {
w . Write ([] byte ( "welcome" ))
})
http . ListenAndServe ( ":3000" , r )
}
REST预览:
这是Chi路由的样子的一些预览。还要查看JSON(routes.json)和Markdown(rutes.md)中生成的路由文档。
我强烈建议阅读上面列出的示例的来源,它们将向您展示Chi的所有功能,并作为一种良好的文档形式。
import (
//...
"context"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main () {
r := chi . NewRouter ()
// A good base middleware stack
r . Use ( middleware . RequestID )
r . Use ( middleware . RealIP )
r . Use ( middleware . Logger )
r . Use ( middleware . Recoverer )
// Set a timeout value on the request context (ctx), that will signal
// through ctx.Done() that the request has timed out and further
// processing should be stopped.
r . Use ( middleware . Timeout ( 60 * time . Second ))
r . Get ( "/" , func ( w http. ResponseWriter , r * http. Request ) {
w . Write ([] byte ( "hi" ))
})
// RESTy routes for "articles" resource
r . Route ( "/articles" , func ( r chi. Router ) {
r . With ( paginate ). Get ( "/" , listArticles ) // GET /articles
r . With ( paginate ). Get ( "/{month}-{day}-{year}" , listArticlesByDate ) // GET /articles/01-16-2017
r . Post ( "/" , createArticle ) // POST /articles
r . Get ( "/search" , searchArticles ) // GET /articles/search
// Regexp url parameters:
r . Get ( "/{articleSlug:[a-z-]+}" , getArticleBySlug ) // GET /articles/home-is-toronto
// Subrouters:
r . Route ( "/{articleID}" , func ( r chi. Router ) {
r . Use ( ArticleCtx )
r . Get ( "/" , getArticle ) // GET /articles/123
r . Put ( "/" , updateArticle ) // PUT /articles/123
r . Delete ( "/" , deleteArticle ) // DELETE /articles/123
})
})
// Mount the admin sub-router
r . Mount ( "/admin" , adminRouter ())
http . ListenAndServe ( ":3333" , r )
}
func ArticleCtx ( next http. Handler ) http. Handler {
return http . HandlerFunc ( func ( w http. ResponseWriter , r * http. Request ) {
articleID := chi . URLParam ( r , "articleID" )
article , err := dbGetArticle ( articleID )
if err != nil {
http . Error ( w , http . StatusText ( 404 ), 404 )
return
}
ctx := context . WithValue ( r . Context (), "article" , article )
next . ServeHTTP ( w , r . WithContext ( ctx ))
})
}
func getArticle ( w http. ResponseWriter , r * http. Request ) {
ctx := r . Context ()
article , ok := ctx . Value ( "article" ).( * Article )
if ! ok {
http . Error ( w , http . StatusText ( 422 ), 422 )
return
}
w . Write ([] byte ( fmt . Sprintf ( "title:%s" , article . Title )))
}
// A completely separate router for administrator routes
func adminRouter () http. Handler {
r := chi . NewRouter ()
r . Use ( AdminOnly )
r . Get ( "/" , adminIndex )
r . Get ( "/accounts" , adminListAccounts )
return r
}
func AdminOnly ( next http. Handler ) http. Handler {
return http . HandlerFunc ( func ( w http. ResponseWriter , r * http. Request ) {
ctx := r . Context ()
perm , ok := ctx . Value ( "acl.permission" ).( YourPermissionType )
if ! ok || ! perm . IsAdmin () {
http . Error ( w , http . StatusText ( 403 ), 403 )
return
}
next . ServeHTTP ( w , r )
})
}
Chi的路由器基于一种Patricia Radix Trie。路由器与net/http
完全兼容。
在树顶上建造的是Router
接口:
// Router consisting of the core routing methods used by chi's Mux,
// using only the standard net/http.
type Router interface {
http. Handler
Routes
// Use appends one or more middlewares onto the Router stack.
Use ( middlewares ... func (http. Handler ) http. Handler )
// With adds inline middlewares for an endpoint handler.
With ( middlewares ... func (http. Handler ) http. Handler ) Router
// Group adds a new inline-Router along the current routing
// path, with a fresh middleware stack for the inline-Router.
Group ( fn func ( r Router )) Router
// Route mounts a sub-Router along a `pattern`` string.
Route ( pattern string , fn func ( r Router )) Router
// Mount attaches another http.Handler along ./pattern/*
Mount ( pattern string , h http. Handler )
// Handle and HandleFunc adds routes for `pattern` that matches
// all HTTP methods.
Handle ( pattern string , h http. Handler )
HandleFunc ( pattern string , h http. HandlerFunc )
// Method and MethodFunc adds routes for `pattern` that matches
// the `method` HTTP method.
Method ( method , pattern string , h http. Handler )
MethodFunc ( method , pattern string , h http. HandlerFunc )
// HTTP-method routing along `pattern`
Connect ( pattern string , h http. HandlerFunc )
Delete ( pattern string , h http. HandlerFunc )
Get ( pattern string , h http. HandlerFunc )
Head ( pattern string , h http. HandlerFunc )
Options ( pattern string , h http. HandlerFunc )
Patch ( pattern string , h http. HandlerFunc )
Post ( pattern string , h http. HandlerFunc )
Put ( pattern string , h http. HandlerFunc )
Trace ( pattern string , h http. HandlerFunc )
// NotFound defines a handler to respond whenever a route could
// not be found.
NotFound ( h http. HandlerFunc )
// MethodNotAllowed defines a handler to respond whenever a method is
// not allowed.
MethodNotAllowed ( h http. HandlerFunc )
}
// Routes interface adds two methods for router traversal, which is also
// used by the github.com/go-chi/docgen package to generate documentation for Routers.
type Routes interface {
// Routes returns the routing tree in an easily traversable structure.
Routes () [] Route
// Middlewares returns the list of middlewares in use by the router.
Middlewares () Middlewares
// Match searches the routing tree for a handler that matches
// the method/path - similar to routing a http request, but without
// executing the handler thereafter.
Match ( rctx * Context , method , path string ) bool
}
每种路由方法都接受URL pattern
和handlers
链。 URL模式支持名为params(即/users/{userID}
)和通配符(即/admin/*
)。可以通过调用命名参数的chi.URLParam(r, "userID")
在运行时获取URL参数,而chi.URLParam(r, "*")
以获取通配符参数。
Chi的中间Wares只是STDLIB NET/HTTP中间件处理程序。它们没有什么特别的,这意味着路由器和所有工具都设计为与社区中任何中间件都兼容和友好。这提供了更好的可扩展性和包装的重复使用,并且是Chi目的的核心。
这是标准NET/HTTP中间件的示例,我们在其中分配了"user"
"123"
的上下文密钥。该中间件在请求上下文上设置了一个假设的用户标识符,并调用链中的下一个处理程序。
// HTTP middleware setting a value on the request context
func MyMiddleware ( next http. Handler ) http. Handler {
return http . HandlerFunc ( func ( w http. ResponseWriter , r * http. Request ) {
// create new context from `r` request context, and assign key `"user"`
// to value of `"123"`
ctx := context . WithValue ( r . Context (), "user" , "123" )
// call the next handler in the chain, passing the response writer and
// the updated request object with the new context value.
//
// note: context.Context values are nested, so any previously set
// values will be accessible as well, and the new `"user"` key
// will be accessible from this point forward.
next . ServeHTTP ( w , r . WithContext ( ctx ))
})
}
CHI使用标准NET/HTTP请求处理程序。这个小片段是http.handler func的一个示例,该函数从请求上下文中读取用户标识符 - 假设地,识别了用户发送已验证的请求的用户,该请求已由以前的中间软件处理程序验证+设置。
// HTTP handler accessing data from the request context.
func MyRequestHandler ( w http. ResponseWriter , r * http. Request ) {
// here we read from the request context and fetch out `"user"` key set in
// the MyMiddleware example above.
user := r . Context (). Value ( "user" ).( string )
// respond to the client
w . Write ([] byte ( fmt . Sprintf ( "hi %s" , user )))
}
Chi的路由器解析和将URL参数存储在请求上下文中。这是如何访问NET/HTTP处理程序中的URL参数的示例。当然,Middlewares能够访问相同的信息。
// HTTP handler accessing the url routing parameters.
func MyRequestHandler ( w http. ResponseWriter , r * http. Request ) {
// fetch the url parameter `"userID"` from the request of a matching
// routing pattern. An example routing pattern could be: /users/{userID}
userID := chi . URLParam ( r , "userID" )
// fetch `"key"` from the request context
ctx := r . Context ()
key := ctx . Value ( "key" ).( string )
// respond to the client
w . Write ([] byte ( fmt . Sprintf ( "hi %v, %v" , userID , key )))
}
Chi配备了可选的middleware
包,提供了一套标准的net/http
中间件。请注意,生态系统中与net/http
兼容的任何中间件都可以与Chi的Mux一起使用。
卡/中间件处理程序 | 描述 |
---|---|
允许登录编码 | 执行请求内容编码标题的白名单 |
允许conttype | 接受请求内容类型的明确白名单 |
基本auth | 基本的HTTP身份验证 |
压缩 | 接受压缩响应的客户的GZIP压缩 |
ContentCharset | 确保内容类型请求标头的char集 |
清洁路径 | 从请求路径清洁双重斜线 |
gethead | 自动路由未定义的头部请求以获取处理程序 |
心跳 | 监视端点以检查服务器脉冲 |
记录器 | 将每个请求的开始和结尾记录到经过的处理时间 |
nocache | 设置响应标题以防止客户缓存 |
剖面 | 轻松将NET/HTTP/PPROF连接到路由器 |
Realip | 将http.request的remoteaddr设置为x-real-ip或x-forded-for |
恢复器 | 优雅地吸收恐慌并打印堆栈痕迹 |
requestId | 将请求ID注入每个请求的上下文 |
重定向刷子 | 在路由路径上的重定向斜线 |
路线头 | 路线处理请求标题 |
Setheader | 短途中间件设置响应标头键/值 |
脱衣舞 | 划线在路由路径上 |
日落 | 日落设置折旧/日落标头响应 |
风门 | 在并发请求的数量上提起上限 |
暂停 | 到达超时截止日期时的请求上下文信号 |
urlformat | 从URL解析扩展并将其放在请求上下文 |
带有价值 | 短途中间件在请求上下文上设置密钥/值 |
请参阅https://github.com/go-chi有关其他软件包。
包裹 | 描述 |
---|---|
科尔斯 | 跨原生资源共享(CORS) |
Docgen | 在运行时打印Chi.Router路线 |
jwtauth | JWT身份验证 |
霍斯特劳特 | 基于域/主机的请求路由 |
httplog | 小但功能强大的结构化HTTP请求记录 |
httprate | HTTP请求率限制器 |
httptracer | HTTP请求性能跟踪库 |
httpvcr | 为外部资源编写确定性测试 |
踩踏 | HTTP请求Coalescer |
context
是一个微小的pkg,可提供简单的接口,以跨呼叫堆栈和goroutines发出信号上下文。它最初是由Sameer Ajmani撰写的,自GO1.7以来就可以在Stdlib中使用。
在https://blog.golang.org/context上了解更多信息
和..
基准套件:https://github.com/pkieltyka/go-http-routing-benchmark
截至2020年11月29日,Linux AMD 3950X上的结果为1.15.5
BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op
BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op
BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op
BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op
与其他路由器的比较:https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc
注意:上面的基准中的同差来自呼叫http.request WithContext(context.Context)
方法,该方法克隆http.request,在重复的(alloc'd)请求(alloc'd)上设置Context()
,并将其返回新请求的新请求请求目的。这就是在GO中的请求上设置上下文的方式。
我们很高兴看到您的贡献!
Chi只是一个HTTP路由器,可让您分解为许多较小层的请求处理。许多公司使用Chi为公共API编写休息服务。但是,REST只是通过HTTP管理状态的惯例,编写完整的客户端服务器系统或微服务网络还需要许多其他作品。
除了休息之外,我还建议该领域的一些新作品:
版权(c)2015年至今的彼得·基尔蒂卡(Peter Kieltyka)
根据MIT许可许可