참고 : Gongular는 최근에 업데이트되었으며 이전 버전을 찾고 있다면 V.1.0으로 태그가 지정됩니다.
Gongular는 API를 쉽게 개발하기위한 HTTP 서버 프레임 워크입니다. 그것은 Gin Gonic과 유사하지만 각도와 같은 (또는 스프링과 같은) 종속성 주입 및 더 나은 입력 처리 기능이 특징입니다. 대부분의 경우, 사용자 입력은 구조화 된 데이터로 변환되어야하며 검증되어야합니다. 너무 많은 시간이 걸리고 반복적 인 작업입니다. Gongular는 TAG 기반 유효성 검사를 통해 요청 입력 매핑을 제공함으로써 복잡성을 줄이는 것을 목표로합니다.
참고 : 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 Field를 사용하면 JSON 본체에 매핑 할 수 있으며 Form Field는 파일과 함께 양식 제출에 바인딩 할 수 있습니다.
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는 경로에서 지원됩니다. 유효한 구조 태그를 사용하여 매개 변수를 검증 할 수 있습니다.
type PathParamHandler struct {
Param struct {
Username string
}
}
func ( p * PathParamHandler ) Handle ( c * Context ) error {
c . SetBody ( p . Param . Username )
return nil
} 쿼리 매개 변수는 경로 매개 변수와 매우 유사하며 필드 이름의 유일한 차이는 Query 이어야하며 내부 매개 변수 나 배열이없는 평평한 구조물이어야합니다. 쿼리 매개 변수는 사례에 민감하며 기본적으로 구조물 속성의 정확한 이름을 사용합니다. 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
} Gongular는 요청 본문과 무엇을 해야하는지 혼동하기 때문에 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)가 원인으로 사용자에게 반환됩니다. 검증은 쿼리, 매개 변수, 본문 또는 양식 유형 입력에서 사용할 수 있습니다. 예는 다음과 같이 볼 수 있습니다.
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를 만드는 것은 경로 처리기에 안전한 가치 주입을 제공한다는 것입니다. 핸들러에서 유동성을 높이기를 원하는 데이터베이스 연결 또는 다른 외부 유틸리티를 저장하는 데 사용될 수 있지만 공간을 오염시킬 수있는 다른 글로벌 기능에서 얻을 수 없거나 원하지 않습니다. 공급 된 종속성은 핸들러를 노선하기 위해 AS-로 제공되며 공급 라우터에 비공개이며 글로벌은 없습니다.
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 {})기본 주입은 훌륭하게 작동하지만 동일한 유형의 값을 두 번 이상 공급하려면 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 {}) 때로는 값을 제공하는 것만으로는 충분하지 않을 수 있습니다. 데이터베이스를 핑하고 트랜잭션을 작성하고 풀에서 값을 얻으려면 사용자 정의 로직을 구현해야합니다. 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가 아닌 다른 사람이 응답을 처리하기 때문에 사용해서는 안됩니다.context.Logger() : 컨텍스트의 로거를 반환합니다. 엔진 용으로 전 세계적으로 설정된 Route Callback을 사용하면 완료된 요청에 대한 통계를 얻을 수 있습니다. 여기에는 요청 로그 및 일치하는 핸들러를 포함한 공통 정보, 각 핸들러의 시간 금액, 총 시간, 총 응답 크기 작성 및 최종 상태 코드가 포함되어 있으며, 이는 다른 모니터링 서비스로 보내거나 로그 분석을위한 탄성 검색에 유용 할 수 있습니다.
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는 쿠키를 설정할 수있는 http.header로 응답하기위한 것입니다. 원하지 않으면 생략 할 수 있습니다.
Websockethandler의 좋은 점은 Param 및 Query 요청도 지원하여 요청 전에 모든 바인딩 및 검증을 수행 할 수 있으며 처리기에서 사용할 수 있다는 것입니다.
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 ()
}