หมายเหตุ: Gongular เพิ่งอัปเดตและหากคุณกำลังมองหาเวอร์ชันก่อนหน้าจะถูกแท็กเป็น v.1.0
Gongular เป็นเฟรมเวิร์กเซิร์ฟเวอร์ HTTP สำหรับการพัฒนา API อย่างง่ายดาย มันเป็นเหมือน Gin Gonic แต่มีการฉีดขึ้นอยู่กับการพึ่งพาแบบเชิงมุม (หรือสปริงเหมือน) และการจัดการอินพุตที่ดีขึ้น เวลาส่วนใหญ่อินพุตผู้ใช้จะต้องแปลงเป็นข้อมูลที่มีโครงสร้างแล้วจะต้องได้รับการตรวจสอบแล้ว ต้องใช้เวลามากเกินไปและเป็นงานซ้ำ ๆ Gongular มีจุดมุ่งหมายเพื่อลดความซับซ้อนนั้นโดยการจัดทำแผนที่การร้องขอด้วยการตรวจสอบความถูกต้องตามแท็ก
หมายเหตุ: Gongular เป็นกรอบความคิดเห็นและต้องอาศัยการสะท้อนอย่างหนักเพื่อให้บรรลุการทำงานเหล่านี้ ในขณะที่มีการทดสอบเพื่อให้แน่ใจว่ามันทำงานได้อย่างไร้ที่ติฉันเปิดรับการมีส่วนร่วมและความคิดเห็นเกี่ยวกับวิธีการทำให้ดีขึ้น
Gongular มีจุดมุ่งหมายที่จะเรียบง่ายให้มากที่สุดเท่าที่จะเป็นไปได้ในขณะที่ให้ความยืดหยุ่น ตัวอย่างด้านล่างนั้นเพียงพอที่จะตอบกลับผู้ใช้ด้วย IP
type WelcomeMessage struct {}
func ( w * WelcomeMessage ) Handle ( c * gongular. Context ) error {
c . SetBody ( c . Request (). RemoteAddr )
}
g := gongular . NewEngine ()
g . GET ( "/" , & WelcomeMessage {})
g . ListenAndServe ( ":8000" ) ตัวจัดการ HTTP ทั้งหมดใน gongular เป็น structs ที่มี Handle(c *gongular.Context) error หรือในคำอื่น ๆ อินเตอร์เฟส RequestHandler นำไปใช้ คำขอของตัวจัดการมีความยืดหยุ่น พวกเขาสามารถมีฟิลด์ต่าง ๆ ซึ่งบางเขตข้อมูลที่มีชื่อเฉพาะมีความพิเศษ ตัวอย่างเช่นหากคุณต้องการผูกพารามิเตอร์พา ธ วัตถุตัวจัดการของคุณจะต้องมีฟิลด์ชื่อ Param ซึ่งเป็นโครงสร้างแบน นอกจากนี้คุณยังสามารถมีฟิลด์ Query ซึ่งแมปเพื่อสืบค้นพารามิเตอร์ Body Field ช่วยให้คุณแมปกับร่างกาย JSON และฟิลด์ Form ช่วยให้คุณผูกเข้ากับการส่งแบบฟอร์มด้วยไฟล์
type MyHandler struct {
Param struct {
UserID int
}
Query struct {
Name string
Age int
Level float64
}
Body struct {
Comment string
Choices [] string
Address struct {
City string
Country string
Hello string
}
}
}
func ( m * MyHandler ) Handle ( c * gongular. Context ) error {
c . SetBody ( "Wow so much params" )
return nil
}เราใช้ julienschmidt/httprouter กับคำขอหลายกรัมและทำการผูกพารามิเตอร์กับคำขอ ดังนั้นรูปแบบ: variablename, *somePath ได้รับการสนับสนุนในเส้นทาง โปรดทราบว่าคุณสามารถใช้แท็ก struct ที่ถูกต้องเพื่อตรวจสอบพารามิเตอร์
type PathParamHandler struct {
Param struct {
Username string
}
}
func ( p * PathParamHandler ) Handle ( c * Context ) error {
c . SetBody ( p . Param . Username )
return nil
} พารามิเตอร์การสืบค้นนั้นคล้ายกับพารามิเตอร์เส้นทางความแตกต่างเพียงชื่อฟิลด์ควรเป็น Query และควรเป็นโครงสร้างแบนที่ไม่มีพารามิเตอร์ภายในหรืออาร์เรย์ พารามิเตอร์แบบสอบถามเป็นตัวพิมพ์เล็กและใช้ชื่อที่แน่นอนของคุณสมบัติ struct โดยค่าเริ่มต้น คุณสามารถใช้แท็ก q struct เพื่อระบุคีย์พารามิเตอร์
type QueryParamHandler struct {
Query struct {
Username string `q:"username"`
Age int
}
}
func ( p * QueryParamHandler ) Handle ( c * Context ) error {
println ( p . Param . Age )
c . SetBody ( p . Param . Username )
return nil
}ร่างกายคำขอ JSON สามารถแยกวิเคราะห์ได้คล้ายกับพารามิเตอร์การสืบค้น แต่ร่างกาย JSON สามารถเป็นโครงสร้างโดยพลการ
type BodyParamHandler struct {
Body struct {
Username string
Age int
Preferences [] string
Comments [] struct {
OwnerID int
Message string
}
}
}
func ( p * BodyParamHandler ) Handle ( c * Context ) error {
println ( p . Body . Age )
c . SetBody ( p . Body . Preferences + len ( c . Body . Comments ))
return nil
} โปรดทราบว่า Body และ Form ไม่สามารถอยู่ในตัวจัดการเดียวกันได้เนื่องจาก gongular จะทำให้สับสนว่าจะทำอย่างไรกับร่างกายคำขอ
type formHandler struct {
Form struct {
Age int
Name string
Favorite string
Fraction float64
}
}
func ( q * formHandler ) Handle ( c * Context ) error {
c . SetBody ( fmt . Sprintf ( "%d:%s:%s:%.2f" ,
q . Form . Age , q . Form . Name , q . Form . Favorite , q . Form . Fraction ))
return nil
}
e . GetRouter (). POST ( "/submit" , & formHandler {}) สำหรับไฟล์ที่อัพโหลดเราใช้โครงสร้างพิเศษเพื่อเก็บไว้ในค่าฟอร์มของ request struct UploadedFile ถือ multipart.File และ multipart.Header คุณสามารถทำอะไรก็ได้ที่คุณต้องการกับพวกเขา
type UploadedFile struct {
File multipart. File
Header * multipart. FileHeader
}คุณสามารถใช้มันในตัวจัดการดังต่อไปนี้:
type formUploadTest struct {
Form struct {
SomeFile * UploadedFile
RegularValue int
}
}
func ( f * formUploadTest ) Handle ( c * Context ) error {
s := sha256 . New ()
io . Copy ( s , f . Form . SomeFile . File )
resp := fmt . Sprintf ( "%x:%d" , s . Sum ( nil ), f . Form . RegularValue )
c . SetBody ( resp )
return nil
}
e . GetRouter (). POST ( "/upload" , & formUploadTest {})เส้นทางสามารถมีตัวจัดการหลายตัวเรียกว่ามิดเดิลแวร์ซึ่งอาจเป็นประโยชน์ในการจัดกลุ่มคำขอและทำงานเบื้องต้นก่อนเส้นทางบางเส้นทาง ตัวอย่างเช่นการจัดกลุ่มและการกำหนดเส้นทางต่อไปนี้ใช้ได้:
type simpleHandler struct {}
func ( s * simpleHandler ) Handle ( c * Context ) error {
c . SetBody ( "hi" )
return nil
}
// The middle ware that will fail if you supply 5 as a user ID
type middlewareFailIfUserId5 struct {
Param struct {
UserID int
}
}
func ( m * middlewareFailIfUserId5 ) Handle ( c * Context ) error {
if m . Param . UserID == 5 {
c . Status ( http . StatusTeapot )
c . SetBody ( "Sorry" )
c . StopChain ()
}
return nil
}
r := e . GetRouter ()
g := r . Group ( "/api/user/:UserID" , & middlewareFailIfUserId5 {})
g . GET ( "/name" , & simpleHandler {})
g . GET ( "/wow" , & simpleHandler {})
/*
The example responses:
/api/user/5/name -> Sorry
/api/user/4/name -> hi
/api/user/1/wow -> hi
*/ เราใช้ asaskevich/govalidator เป็นกรอบการตรวจสอบความถูกต้อง หากอินพุตที่ให้มาไม่ผ่านขั้นตอนการตรวจสอบความถูกต้อง http.statusbadrequest (400) จะถูกส่งคืนผู้ใช้ด้วยสาเหตุ การตรวจสอบความถูกต้องสามารถใช้ในการสืบค้นพารามิเตอร์ร่างกายหรือประเภทอินพุต ตัวอย่างสามารถดูได้ดังนี้:
type QueryParamHandler struct {
Query struct {
Username string `valid:"alpha"`
Age int
}
}
func ( p * QueryParamHandler ) Handle ( c * Context ) error {
println ( p . Param . Age )
c . SetBody ( p . Param . Username )
return nil
} หากมีการตั้งค่าคำขอที่มีชื่อผู้ใช้ที่ไม่ถูกต้องจะส่งคืน ParseError
หนึ่งในสิ่งที่ทำให้ gongular จากเฟรมเวิร์กอื่น ๆ คือให้การฉีดคุ้มค่าที่ปลอดภัยไปยังตัวจัดการเส้นทาง มันสามารถใช้ในการจัดเก็บการเชื่อมต่อฐานข้อมูลหรือยูทิลิตี้ภายนอกอื่น ๆ ที่คุณต้องการให้สามารถใช้งานได้ในตัวจัดการของคุณ แต่ไม่ต้องการทำให้เป็นทั่วโลกหรือเพียงแค่ได้รับจากฟังก์ชั่นระดับโลกอื่น ๆ ที่อาจทำให้เกิดมลพิษในพื้นที่ การพึ่งพาที่ให้มานั้นมีให้ในการกำหนดเส้นทางตัวจัดการและพวกเขาเป็นส่วนตัวสำหรับเราเตอร์ที่จัดหาให้ไม่มีอะไรเป็นระดับโลก
Gongular ช่วยให้การฉีดพื้นฐานมาก: คุณให้คุณค่ากับ gongular.engine และให้คุณกับตัวจัดการของคุณหากคุณต้องการในฟังก์ชั่น Handler ของคุณ มันไม่เหมือน guice หรือฤดูใบไม้ผลิเช่นการฉีดมันไม่สามารถแก้ไขการพึ่งพาการฉีดมันได้เพียงให้ค่าเพื่อให้คุณไม่ใช้ค่าทั่วโลกและทำให้การทดสอบง่ายขึ้นเนื่องจากคุณสามารถทดสอบฟังก์ชั่นตัวจัดการของคุณโดยการเยาะเย้ยอินเทอร์เฟซที่คุณชอบ
type myHandler struct {
Param struct {
UserID uint
}
Database * sql. DB
}
func ( i * myHandler ) Handle ( c * Context ) error {
c . SetBody ( fmt . Sprintf ( "%p:%d" , i . Database , i . Param . UserID ))
return nil
}
db := new (sql. DB )
e . Provide ( db )
e . GetRouter (). GET ( "/my/db/interaction/:UserID" , & myHandler {})การฉีดพื้นฐานใช้งานได้ดี แต่ถ้าคุณต้องการจัดหาค่าชนิดเดียวกันมากกว่าหนึ่งครั้งคุณต้องใช้การฉีดคีย์เพื่อให้ gongular แตกต่างกัน
type injectKey struct {
Val1 int `inject:"val1"`
Val2 int `inject:"val2"`
}
func ( i * injectKey ) Handle ( c * Context ) error {
c . SetBody ( i . Val1 * i . Val2 )
return nil
}
e . ProvideWithKey ( "val1" , 71 )
e . ProvideWithKey ( "val2" , 97 )
e . GetRouter (). GET ( "/" , & injectKey {}) บางครั้งการให้ค่านิยมที่อาจไม่เพียงพอสำหรับคุณ คุณสามารถเลือกที่จะ ping ฐานข้อมูลสร้างธุรกรรมรับค่าจากพูลและสิ่งเหล่านี้ต้องการการใช้ตรรกะที่กำหนดเอง Gongular ช่วยให้คุณสามารถเขียน CustomProvideFunction ซึ่งช่วยให้คุณสามารถให้ค่าที่คุณต้องการด้วยตรรกะใด ๆ ที่คุณต้องการ
type injectCustom struct {
DB * sql. DB
}
func ( i * injectCustom ) Handle ( c * Context ) error {
c . SetBody ( fmt . Sprintf ( "%p" , i . DB ))
return nil
}
e := newEngineTest ()
var d * sql. DB
e . CustomProvide ( & sql. DB {}, func ( c * Context ) ( interface {}, error ) {
d = new (sql. DB )
return d , nil
})
e . GetRouter (). GET ( "/" , & injectCustom {}) ฟังก์ Provide ที่ให้เริ่มต้นจะช่วยให้คุณสามารถฉีดการใช้งานได้เท่านั้น การฉีดอินเทอร์เฟซจะไม่ทำงาน ในระหว่างการฉีดหัวฉีดจะค้นหาประเภทและความล้มเหลวที่ให้ไว้ ตัวอย่างเช่นรหัสต่อไปนี้จะไม่ทำงาน:
type injectKey struct {
DB MySQLInterface `inject:"db"`
}
func ( i * injectKey ) Handle ( c * Context ) error {
c . SetBody ( "yay" )
return nil
}
e . ProvideWithKey ( "db" , & sql. DB {})
e . GetRouter (). GET ( "/" , & injectKey {}) สิ่งนี้จะทำให้เกิดข้อผิดพลาดของหัวฉีด หากคุณต้องการฉีดอินเทอร์เฟซคุณต้องใช้ ProvideUnsafe ProvideUnsafe เป็นการฉีดคีย์/ค่าที่เข้มงวด คุณไม่สามารถระบุหลายค่าสำหรับคีย์เดียวกัน
ตัวอย่างการใช้งาน:
type injectKey struct {
DB MySQLInterface `inject:"db"`
}
func ( i * injectKey ) Handle ( c * Context ) error {
c . SetBody ( "yay" )
return nil
}
e . ProvideUnsafe ( "db" , initializeDB ())
// This would cause a panic
// e.ProvideUnsafe("db", &sql.DB{})
e . GetRouter (). GET ( "/" , & injectKey {})context.SetBody(interface{}) : ตั้งค่าการตอบสนองของร่างกายที่จะเป็นอนุกรมcontext.Status(int) : ตั้งค่าสถานะของการตอบกลับหากไม่ได้ตั้งค่าก่อนหน้านี้context.MustStatus(int) : แทนที่สถานะที่เขียนก่อนหน้านี้context.Request() : ส่งคืนคำขอ HTTP ดิบพื้นฐานcontext.Header(string,string) : ตั้งส่วนหัวการตอบกลับที่กำหนดcontext.Finalize() : ใช้ในการเขียนการตอบสนองต่อลูกค้าโดยปกติไม่ควรใช้นอกเหนือจากใน Panichandler เนื่องจาก gongular ดูแลการตอบสนองcontext.Logger() : ส่งคืนเครื่องบันทึกของบริบท การโทรกลับเส้นทางตั้งค่าทั่วโลกสำหรับเครื่องยนต์ช่วยให้คุณได้รับสถิติสำหรับคำขอที่เสร็จสมบูรณ์ มันมีข้อมูลทั่วไปรวมถึงบันทึกการร้องขอและตัวจัดการที่ตรงกันระยะเวลาที่ใช้ในแต่ละตัวจัดการแต่ละเวลาเวลาทั้งหมดการตอบกลับทั้งหมดที่เขียนและรหัสสถานะสุดท้ายซึ่งจะเป็นประโยชน์สำหรับคุณที่จะส่งไปยังบริการตรวจสอบอื่นหรือเพียงแค่การวิเคราะห์บันทึก
type RouteStat struct {
Request * http. Request
Handlers [] HandlerStat
MatchedPath string
TotalDuration time. Duration
ResponseSize int
ResponseCode int
Logs * bytes. Buffer
} ในกรณีที่คุณส่งคืนข้อผิดพลาดจากฟังก์ชั่นของคุณหรือข้อผิดพลาดอื่นเกิดขึ้นซึ่งทำให้คำขอไม่พอใจ, gongular.Engine เรียกใช้ฟังก์ชันตัวจัดการข้อผิดพลาดซึ่งค่าเริ่มต้นไปยังตัวจัดการต่อไปนี้:
var defaultErrorHandler = func ( err error , c * Context ) {
c . logger . Println ( "An error has occurred:" , err )
switch err := err .( type ) {
case InjectionError :
c . MustStatus ( http . StatusInternalServerError )
c . logger . Println ( "Could not inject the requested field" , err )
case ValidationError :
c . MustStatus ( http . StatusBadRequest )
c . SetBody ( map [ string ] interface {}{ "ValidationError" : err })
case ParseError :
c . MustStatus ( http . StatusBadRequest )
c . SetBody ( map [ string ] interface {}{ "ParseError" : err })
default :
c . SetBody ( err . Error ())
c . MustStatus ( http . StatusInternalServerError )
}
c . StopChain ()
} Gongular รองรับการเชื่อมต่อ WebSocket เช่นกัน ฟังก์ชั่น Handler นั้นคล้ายกับอินเตอร์เฟส Handler Route ปกติ แต่ยังอนุญาตให้ยกเลิกการเชื่อมต่อได้หากคุณต้องการด้วยตัวจัดการ Before
type WebsocketHandler interface {
Before ( c * Context ) (http. Header , error )
Handle ( conn * websocket. Conn )
}ก่อนอื่นฟังก์ชั่นการจัดการจะไม่ส่งคืนข้อผิดพลาดเนื่องจากเป็นการดำเนินการอย่างต่อเนื่อง ผู้ใช้รับผิดชอบการโต้ตอบ WebSocket ทั้งหมด ประการที่สองก่อนที่จะใช้ตัวกรองก่อนที่จะอัพเกรดคำขอเป็น WebSocket มันจะมีประโยชน์สำหรับการกรองคำขอและการส่งคืนข้อผิดพลาดจะไม่เปิด WebSocket แต่ปิดด้วยข้อผิดพลาด http.header สำหรับการตอบด้วย http.header ซึ่งอนุญาตให้ตั้งค่าคุกกี้ สามารถละเว้นได้หากไม่ต้องการ
สิ่งที่ดีเกี่ยวกับ WebSockethandler คือการสนับสนุนคำขอพารามิเตอร์และการสืบค้นเช่นกันเพื่อให้การเชื่อมโยงและการตรวจสอบทั้งหมดสามารถทำได้ก่อนการร้องขอและคุณสามารถใช้ในตัวจัดการของคุณ
type wsTest struct {
Param struct {
UserID int
}
Query struct {
Track bool
Username string
}
}
func ( w * wsTest ) Before ( c * Context ) (http. Header , error ) {
return nil , nil
}
func ( w * wsTest ) Handle ( conn * websocket. Conn ) {
_ , msg , err := conn . ReadMessage ()
if err != nil {
conn . Close ()
}
toSend := fmt . Sprintf ( "%s:%d:%s:%t" , msg , w . Param . UserID , w . Query . Username , w . Query . Track )
conn . WriteMessage ( websocket . TextMessage , [] byte ( toSend ))
conn . Close ()
}