chi
เป็นเราเตอร์ที่มีน้ำหนักเบาและเป็นสำนวนสำหรับการสร้างบริการ HTTP เป็นเรื่องดีโดยเฉพาะอย่างยิ่งที่จะช่วยให้คุณเขียนบริการ API REST ขนาดใหญ่ที่ได้รับการบำรุงรักษาเมื่อโครงการของคุณเติบโตและเปลี่ยนแปลง chi
ถูกสร้างขึ้นบนแพ็คเกจ context
ใหม่ที่แนะนำใน GO 1.7 เพื่อจัดการกับการส่งสัญญาณการยกเลิกและค่าที่คาดการณ์ไว้ในห่วงโซ่ตัวจัดการ
จุดสนใจของโครงการคือการค้นหาการออกแบบที่สง่างามและสะดวกสบายสำหรับการเขียนเซิร์ฟเวอร์ REST API ที่เขียนขึ้นระหว่างการพัฒนาบริการ Pressly API ที่ให้บริการบริการ API สาธารณะของเรา
ข้อควรพิจารณาที่สำคัญของการออกแบบของ Chi คือ: โครงสร้างโครงการ, การบำรุงรักษา, ตัวจัดการ HTTP มาตรฐาน (stdlib-only), ผลผลิตของนักพัฒนาและการแยกแยะระบบขนาดใหญ่เป็นส่วนเล็ก ๆ มากมาย เราเตอร์หลัก github.com/go-chi/chi
มีขนาดค่อนข้างเล็ก (น้อยกว่า 1,000 loc) แต่เรายังได้รวมแพคเกจย่อยที่มีประโยชน์/เป็นตัวเลือกบางอย่าง: มิดเดิลแวร์เรนเดอร์และ docgen เราหวังว่าคุณจะสนุกกับมันเช่นกัน!
go get -u github.com/go-chi/chi/v5
net/http
context
ใหม่ให้การผูกมัดมูลค่าการยกเลิกและการหมดเวลาdocgen
Auto -Generates จากแหล่งที่มาของคุณไปยัง 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 )
}
ตัวอย่างที่เหลือ:
นี่คือตัวอย่างเล็ก ๆ น้อย ๆ ของการกำหนดเส้นทางที่ดูเหมือนกับ Chi ดูเอกสารการกำหนดเส้นทางที่สร้างขึ้นใน JSON (route.json) และใน markdown (route.md)
ฉันขอแนะนำให้อ่านแหล่งที่มาของตัวอย่างที่ระบุไว้ข้างต้นพวกเขาจะแสดงคุณสมบัติทั้งหมดของไคและทำหน้าที่เป็นเอกสารที่ดี
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
}
แต่ละวิธีการกำหนดเส้นทางยอมรับ pattern
URL และห่วงโซ่ของ handlers
รูปแบบ URL รองรับพารามิเตอร์ชื่อ (เช่น /users/{userID}
) และ wildcards (เช่น. /admin/*
) พารามิเตอร์ URL สามารถดึงได้ที่รันไทม์โดยเรียก chi.URLParam(r, "userID")
สำหรับพารามิเตอร์ชื่อและ chi.URLParam(r, "*")
สำหรับพารามิเตอร์ไวด์การ์ด
Middlewares ของ Chi เป็นเพียงตัวจัดการ Middleware 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 ลงในบริบทคำขอ นี่คือตัวอย่างของวิธีการเข้าถึงพารามิเตอร์ URL ในตัวจัดการ NET/HTTP ของคุณ และแน่นอน 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
Middlewares มาตรฐาน โปรดทราบว่ามิดเดิลแวร์ใด ๆ ในระบบนิเวศที่เข้ากันได้กับ net/http
สามารถใช้กับ MUX ของ Chi ได้
Handler Chi/Middleware | คำอธิบาย |
---|---|
อนุญาตให้ใช้งาน | บังคับใช้ส่วนหัวการเข้ารหัสเนื้อหาการร้องขอการร้องขอ |
allowcontenttype | ผู้อนุญาตให้มีการอนุญาตอย่างชัดเจนของประเภทเนื้อหาที่ได้รับการยอมรับ |
ขั้นพื้นฐาน | การตรวจสอบ HTTP พื้นฐาน |
บีบอัด | การบีบอัด GZIP สำหรับลูกค้าที่ยอมรับคำตอบที่ถูกบีบอัด |
contentCharset | ตรวจสอบชุด Charset สำหรับส่วนหัวคำขอประเภทเนื้อหา |
ความสะอาด | ทำความสะอาดสแลชสองครั้งจากเส้นทางคำขอ |
คนขี้เกียจ | กำหนดเส้นทางคำขอหัวที่ไม่ได้กำหนดโดยอัตโนมัติเพื่อรับตัวจัดการ |
การเต้นของหัวใจ | การตรวจสอบจุดสิ้นสุดเพื่อตรวจสอบชีพจรเซิร์ฟเวอร์ |
คนตัดไม้ | บันทึกการเริ่มต้นและสิ้นสุดของแต่ละคำขอด้วยเวลาประมวลผลที่ผ่านไป |
nocache | ตั้งส่วนหัวการตอบกลับเพื่อป้องกันลูกค้าจากการแคช |
ผู้ทำโปรไฟล์ | แนบ NET/HTTP/PPROF ได้อย่างง่ายดายกับเราเตอร์ของคุณ |
realip | ตั้งค่า remoteaddr ของ http.request เป็น x-real-ip หรือ x-forwarded-for |
ผู้กู้คืน | ดูดซับความตื่นตระหนกอย่างสง่างามและพิมพ์ร่องรอยสแต็ก |
การร้องขอ | ฉีดรหัสคำขอลงในบริบทของแต่ละคำขอ |
เปลี่ยนเส้นทาง | เปลี่ยนเส้นทาง Slashes บนเส้นทางการกำหนดเส้นทาง |
หัวรุนแรง | การจัดการเส้นทางสำหรับส่วนหัวคำขอ |
คนทำผม | มิดเดิลแวร์มือสั้นเพื่อตั้งค่าปุ่ม/ค่าส่วนหัวตอบกลับ |
Stripslashes | แถบสแลชบนเส้นทางการกำหนดเส้นทาง |
พระอาทิตย์ตก | Sunset Set Depecation/Sunset Header เพื่อตอบสนอง |
คันเร่ง | วางเพดานตามจำนวนคำขอพร้อมกัน |
การหมดเวลา | สัญญาณไปยังบริบทคำขอเมื่อถึงกำหนดส่งหมดหมดเวลา |
urlformat | แยกวิเคราะห์ส่วนขยายจาก URL และวางไว้ในบริบทการร้องขอ |
มีค่า | มิดเดิลแวร์มือสั้นเพื่อตั้งค่าคีย์/ค่าในบริบทคำขอ |
โปรดดู https://github.com/go-chi สำหรับแพ็คเกจเพิ่มเติม
บรรจุุภัณฑ์ | คำอธิบาย |
---|---|
ไม้กางเขน | การแบ่งปันทรัพยากรข้ามแหล่งกำเนิด (CORS) |
ขั้ว | พิมพ์เส้นทาง Chi.router ที่รันไทม์ |
jwtouth | การรับรองความถูกต้องของ JWT |
ผู้ลี้ภัย | การกำหนดเส้นทางคำขอตามโดเมน/โฮสต์ |
httplog | การบันทึกคำขอ HTTP ที่มีโครงสร้างขนาดเล็ก แต่ทรงพลัง |
httprate | ตัว จำกัด อัตราการร้องขอ http |
httptracer | ไลบรารีการติดตามประสิทธิภาพการร้องขอ http |
httpvcr | เขียนการทดสอบที่กำหนดขึ้นสำหรับแหล่งข้อมูลภายนอก |
แตกตื่น | http ขอ coalescer |
context
เป็น PKG เล็ก ๆ ที่ให้อินเทอร์เฟซอย่างง่ายเพื่อส่งสัญญาณบริบทข้ามสแต็คการโทรและ goroutines เดิมทีเขียนโดย Sameer Ajmani และมีอยู่ใน Stdlib ตั้งแต่ Go1.7
เรียนรู้เพิ่มเติมได้ที่ https://blog.golang.org/context
และ..
ชุดเบนช์มาร์ก: https://github.com/pkieltyka/go-http-routing-benchmark
ผลลัพธ์ ณ วันที่ 29 พ.ย. 2020 กับ GO 1.15.5 บน Linux AMD 3950X
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's WithContext(context.Context)
ที่โคลน http.request ตั้ง Context()
บนคำขอที่ซ้ำกัน วัตถุ. นี่เป็นเพียงวิธีการตั้งค่าบริบทในการร้องขอใน GO ทำงาน
เราจะมีความสุขมากกว่าที่จะเห็นการมีส่วนร่วมของคุณ!
Chi เป็นเพียงเราเตอร์ HTTP ที่ช่วยให้คุณย่อยสลายการร้องขอการจัดการเป็นเลเยอร์ขนาดเล็กจำนวนมาก หลาย บริษัท ใช้ CHI เพื่อเขียนบริการพักผ่อนสำหรับ API สาธารณะของพวกเขา แต่ REST เป็นเพียงการประชุมสำหรับการจัดการสถานะผ่าน HTTP และมีชิ้นส่วนอื่น ๆ อีกมากมายที่จำเป็นในการเขียนระบบไคลเอนต์เซิร์ฟเวอร์ที่สมบูรณ์หรือเครือข่าย microservices
เมื่อมองไปที่พักผ่อนฉันขอแนะนำงานใหม่ ๆ ในสนาม:
ลิขสิทธิ์ (c) 2015 ปัจจุบัน Peter Kieltyka
ได้รับใบอนุญาตภายใต้ใบอนุญาต MIT