注: Gongularが最近更新されました。以前のバージョンを探している場合は、v.1.0としてタグ付けされています
Gongularは、APIを簡単に開発するためのHTTPサーバーフレームワークです。ジンゴニックのようなものですが、角度のような(または春のような)依存関係の注入とより良い入力処理が特徴です。ほとんどの場合、ユーザー入力は構造化されたデータに変換され、検証する必要があります。時間がかかりすぎて、繰り返しの作業です。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" )GongularのすべてのHTTPハンドラーはHandle(c *gongular.Context) errorを備えた構造体またはRequestHandlerインターフェイスが実装されています。リクエストハンドラーオブジェクトは柔軟です。特定の名前を持つフィールドの一部が特別なさまざまなフィールドを持つことができます。たとえば、パスパラメーターにバインドする場合、ハンドラーオブジェクトには、フラット構造体であるParamという名前のフィールドが必要です。また、パラメーターをクエリするためのマップもマップするQueryフィールドを持つことができます。 Bodyフィールドを使用すると、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同じハンドラーに存在することはできないことに注意してください。
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 {})アップロードされたファイルの場合、特別なファイルを使用して、リクエスト構造のフォーム値にそれらを保持します。 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)が原因でユーザーに返されます。検証は、クエリ、PARAM、ボディ、またはフォームタイプの入力で使用できます。例は次のように見ることができます。
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を返します。
他のフレームワークからゴングラを作ることの1つは、ハンドラーをルーティングするために安全な価値噴射を提供することです。データベース接続、またはそれがハンドラーで熟知したい他の外部ユーティリティを保存するために使用できますが、それをグローバルにしたくない、またはスペースを汚染する可能性のある他のグローバル機能からそれを取得することはできません。供給された依存関係は、ハンドラーをルーティングするために提供され、それらはプライベートに対応するルーターであり、グローバルなものはありません。
Gongularは非常に基本的な注入を可能にします。Gongular.Engineに値を提供し、ハンドラー機能に必要な場合はハンドラーに提供します。注射のようなガイドやバネのようなものではなく、注入の依存関係を解決するのではなく、値を提供するだけなので、グローバルな値を使用せず、テストを容易にします。
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 {})基本的な注入はうまく機能しますが、同じタイプの値を複数回提供したい場合は、ゴングラが異なるようにキー付き注射を使用する必要があります。
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() :基礎となるRAW HTTPリクエストを返しますcontext.Header(string,string) :特定の応答ヘッダーを設定します。context.Finalize() :クライアントへの応答を書くために使用されます。通常、Gongularが応答を処理するため、Panichandler以外に使用しないでください。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接続もサポートしています。ハンドラー関数は通常のルートハンドラーインターフェイスに似ていますが、 Beforeハンドラーを希望する場合は接続終了も可能にします。
type WebsocketHandler interface {
Before ( c * Context ) (http. Header , error )
Handle ( conn * websocket. Conn )
}まず第一に、ハンドル関数は継続的な実行であるため、エラーを返しません。ユーザーは、すべてのWebSocketインタラクションに対して責任を負います。第二に、websocketにリクエストをアップグレードする直前にフィルターが適用されます。リクエストをフィルタリングし、エラーを返すとWebSocketが開かないが、エラーで閉じるのに役立ちます。 http.headerは、Cookieの設定を可能にする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 ()
}