Chi是一个轻量的Web框架。Github,网文介绍可以看这里.
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("Hello World!"))
})
http.ListenAndServe(":3000", r)
}
看起来还是挺简单的。
路由
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)
一个都不能少。
r.Put("/path", myHandler)
/users/{userID} chi.URLParam(r, "userID")
/admin/* chi.URLParam(r, "*")
r.Get("/articles/{rid:^[0-9]{5,6}}", getArticle)
r := chi.NewRouter()
r.Get("/articles/{date}-{slug}", getArticle)
func getArticle(w http.ResponseWriter, r *http.Request) {
dateParam := chi.URLParam(r, "date")
slugParam := chi.URLParam(r, "slug")
article, err := database.GetArticle(date, slug)
if err != nil {
w.WriteHeader(422)
w.Write([]byte(fmt.Sprintf("error fetching article %s-%s: %v", dateParam, slugParam, err)))
return
}
if article == nil {
w.WriteHeader(404)
w.Write([]byte("article not found"))
return
}
w.Write([]byte(article.Text()))
})
自定义404和405
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
w.Write([]byte("route does not exist"))
})
r.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(405)
w.Write([]byte("method is not valid"))
})
子路由
func main(){
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
})
// Creating a New Router
apiRouter := chi.NewRouter()
apiRouter.Get("/articles/{date}-{slug}", getArticle)
// Mounting the new Sub Router on the main router
r.Mount("/api", apiRouter)
}
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
})
})
路由组
通过分组,可以不同组使用不同的中间件 ·
func main(){
r := chi.NewRouter()
// Public Routes
r.Group(func(r chi.Router) {
r.Get("/", HelloWorld)
r.Get("/{AssetUrl}", GetAsset)
r.Get("/manage/url/{path}", FetchAssetDetailsByURL)
r.Get("/manage/id/{path}", FetchAssetDetailsByID)
})
// Private Routes
// Require Authentication
r.Group(func(r chi.Router) {
r.Use(AuthMiddleware)
r.Post("/manage", CreateAsset)
})
}
中间件
此中间件添加了一个上下文变量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))
})
}
中间件下方的程序就可以获取到了
func MyHandler(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)))
}
AllowContentEncoding
内容编码白名单中间件(gzip, deflate, gzip.deflate, deflate.gzip).意思是必须使用这类压缩方式?
import (
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.AllowContentEncoding("deflate", "gzip"))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
}
AllowContentType
允许类型白名单. 只允许某类型的内容. (application/json, text/xml, application/json, text/xml)
import (
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
r.Use(middleware.AllowContentType("application/json","text/xml"))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
}
CleanPath
清洁路径,清除多余的双斜杠 如果用户请求 /users//1 或 //users////1 都将被视为:/users/1
Compress
压缩中间件 确保在响应上设置 Content-Type 标头,否则此中间件将不会压缩响应正文
import (
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
r.Use(middleware.Compress(5, "text/html", "text/css"))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
}
ContentCharset
生成一个处理程序,如果所有字符集都不匹配,则该处理程序将写入415响应
r := chi.NewRouter()
allowedCharsets := []string{"UTF-8", "Latin-1", ""}
r.Use(middleware.ContentCharset(allowedCharsets...))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
CORS
func main() {
r := chi.NewRouter()
// Basic CORS
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
r.Use(cors.Handler(cors.Options{
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
AllowedOrigins: []string{"https://*", "http://*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300, // Maximum value not ignored by any of major browsers
}))
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
http.ListenAndServe(":3000", r)
}
GetHead
自动将未定义的 HEAD 请求路由到 GET 处理程序. 不是太明白意思
func main(){
r := chi.NewRouter()
r.Use(middleware.GetHead)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {})
}
Heartbeat
func main(){
r := chi.NewRouter()
r.Use(middleware.Heartbeat("/"))
}
Logger
记录每个请求的开始和结束,以及一些有用的数据,包括请求的内容、响应状态以及返回所需的时间。
NoCache
设置了许多 HTTP 标头,以防止路由器(或子路由器)被上游代理和/或客户端缓存。
Oauth 2.0
关于授权服务(略过)
Profiler
用于挂载 net/http/pprof
import (
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
// ..middlewares
r.Mount("/debug", middleware.Profiler())
// ..routes
}
RealIP
获取一个真实的IP
Recoverer
从崩溃中恢复,记录崩溃(和回溯),并尽可能返回 HTTP 500(内部服务器错误)状态
RedirectSlashes
减去尾部斜杠的作用? RedirectSlashes 中间件与 http 不兼容
RouteHeaders
StripSlashes
Throttle
用于限制所有用户一次处理的当前请求数。(不是针对某一个用户) 默认情况下,Throttle 的 BacklogTimeout 为 60 秒
import (
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
r.Use(middleware.Throttle(15))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
}
ThrottleBacklog
限制一次处理的当前处理请求数,并为保存有限数量的待处理请求提供积压。
import (
"time"
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
r.Use(ThrottleBacklog(10, 50, time.Second*10))
r.Post("/", func(w http.ResponseWriter, r *http.Request) {})
}
Timeout
在给定超时后取消 ctx 并向客户端返回 504 网关超时错误。
r.Get("/long", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
processTime := time.Duration(rand.Intn(4)+1) * time.Second
select {
case <-ctx.Done():
return
case <-time.After(processTime):
// The above channel simulates some hard work.
}
w.Write([]byte("done"))
})
// 用法
import (
"github.com/go-chi/chi/v5/middleware"
)
func main(){
r := chi.NewRouter()
r.Use(middleware.Timeout(time.Second*60))
// handlers ...
}
JWT身份验证
Http 限速中间件
package main
import (
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/httprate"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
// 启用每分钟100个请求的速率请求限制器
//
// 在下面的代码示例中,速率限制被绑定到请求的IP地址通过LimitByIP中间件处理程序进行
// 要为所有请求设置一个速率限制器,请使用httprate.LimitAll(..)
//
r.Use(httprate.LimitByIP(100, 1*time.Minute))
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("."))
})
http.ListenAndServe(":3333", r)
}
官网文档就这么多,然后就是自己看示例学习
文件服务器示例
package main
import (
"net/http"
"os"
"path/filepath"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
// Index handler
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
})
// Create a route along /files that will serve contents from
// the ./data/ folder.
workDir, _ := os.Getwd()
filesDir := http.Dir(filepath.Join(workDir, "data"))
FileServer(r, "/files", filesDir)
http.ListenAndServe(":3333", r)
}
// FileServer conveniently sets up a http.FileServer handler to serve
// static files from a http.FileSystem.
func FileServer(r chi.Router, path string, root http.FileSystem) {
if strings.ContainsAny(path, "{}*") {
panic("FileServer does not permit any URL parameters.")
}
if path != "/" && path[len(path)-1] != '/' {
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
}
path += "*"
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
fs.ServeHTTP(w, r)
})
}