2014-06-17 23:42:34 +00:00
package gin
import (
2014-07-02 00:31:11 +00:00
"bytes"
2014-06-17 23:42:34 +00:00
"encoding/xml"
2014-07-02 00:32:44 +00:00
"errors"
2014-07-02 00:31:11 +00:00
"fmt"
2014-07-03 17:19:06 +00:00
"github.com/gin-gonic/gin/binding"
2014-07-15 15:41:56 +00:00
"github.com/gin-gonic/gin/render"
2014-06-17 23:42:34 +00:00
"github.com/julienschmidt/httprouter"
"html/template"
"log"
"math"
"net/http"
"path"
2014-07-06 19:09:23 +00:00
"sync"
2014-06-17 23:42:34 +00:00
)
const (
2014-07-08 12:10:27 +00:00
AbortIndex = math . MaxInt8 / 2
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
2014-06-17 23:42:34 +00:00
)
2014-07-07 22:16:41 +00:00
const (
ErrorTypeInternal = 1 << iota
ErrorTypeExternal = 1 << iota
ErrorTypeAll = 0xffffffff
)
2014-06-17 23:42:34 +00:00
type (
HandlerFunc func ( * Context )
H map [ string ] interface { }
2014-07-03 18:30:01 +00:00
// Used internally to collect errors that occurred during an http request.
2014-07-07 22:16:41 +00:00
errorMsg struct {
2014-07-02 00:31:11 +00:00
Err string ` json:"error" `
2014-07-07 22:16:41 +00:00
Type uint32 ` json:"-" `
2014-07-02 00:31:11 +00:00
Meta interface { } ` json:"meta" `
2014-06-17 23:42:34 +00:00
}
2014-07-07 22:16:41 +00:00
errorMsgs [ ] errorMsg
2014-07-02 00:31:11 +00:00
2014-06-17 23:42:34 +00:00
// Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example.
Context struct {
2014-07-08 14:57:04 +00:00
Request * http . Request
2014-07-03 22:01:28 +00:00
Writer ResponseWriter
2014-06-17 23:42:34 +00:00
Keys map [ string ] interface { }
2014-07-07 22:16:41 +00:00
Errors errorMsgs
2014-06-17 23:42:34 +00:00
Params httprouter . Params
2014-07-03 12:11:42 +00:00
Engine * Engine
2014-06-17 23:42:34 +00:00
handlers [ ] HandlerFunc
index int8
}
// Used internally to configure router, a RouterGroup is associated with a prefix
// and an array of handlers (middlewares)
RouterGroup struct {
Handlers [ ] HandlerFunc
prefix string
parent * RouterGroup
engine * Engine
}
2014-07-03 18:30:01 +00:00
// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
2014-06-17 23:42:34 +00:00
Engine struct {
* RouterGroup
2014-07-15 15:41:56 +00:00
HTMLRender render . Render
cache sync . Pool
handlers404 [ ] HandlerFunc
router * httprouter . Router
2014-06-17 23:42:34 +00:00
}
)
2014-07-05 00:51:25 +00:00
// Allows type H to be used with xml.Marshal
func ( h H ) MarshalXML ( e * xml . Encoder , start xml . StartElement ) error {
2014-07-13 14:53:46 +00:00
start . Name = xml . Name {
Space : "" ,
Local : "map" ,
}
2014-07-05 03:18:05 +00:00
if err := e . EncodeToken ( start ) ; err != nil {
return err
}
2014-07-05 00:51:25 +00:00
for key , value := range h {
2014-07-05 03:18:05 +00:00
elem := xml . StartElement {
2014-07-13 14:53:46 +00:00
Name : xml . Name { Space : "" , Local : key } ,
Attr : [ ] xml . Attr { } ,
2014-07-05 03:18:05 +00:00
}
2014-07-06 16:25:17 +00:00
if err := e . EncodeElement ( value , elem ) ; err != nil {
2014-07-05 03:18:05 +00:00
return err
}
}
2014-07-13 14:53:46 +00:00
if err := e . EncodeToken ( xml . EndElement { Name : start . Name } ) ; err != nil {
2014-07-05 03:18:05 +00:00
return err
2014-07-05 00:51:25 +00:00
}
return nil
}
2014-07-07 22:16:41 +00:00
func ( a errorMsgs ) ByType ( typ uint32 ) errorMsgs {
if len ( a ) == 0 {
return a
}
result := make ( errorMsgs , 0 , len ( a ) )
for _ , msg := range a {
if msg . Type & typ > 0 {
result = append ( result , msg )
}
}
return result
}
func ( a errorMsgs ) String ( ) string {
2014-07-12 22:18:33 +00:00
if len ( a ) == 0 {
return ""
}
2014-07-02 00:31:11 +00:00
var buffer bytes . Buffer
for i , msg := range a {
2014-07-04 02:31:11 +00:00
text := fmt . Sprintf ( "Error #%02d: %s \n Meta: %v\n" , ( i + 1 ) , msg . Err , msg . Meta )
2014-07-02 00:31:11 +00:00
buffer . WriteString ( text )
}
return buffer . String ( )
}
2014-07-06 19:09:23 +00:00
// Returns a new blank Engine instance without any middleware attached.
// The most basic configuration
func New ( ) * Engine {
2014-06-17 23:42:34 +00:00
engine := & Engine { }
2014-07-02 09:09:04 +00:00
engine . RouterGroup = & RouterGroup { nil , "/" , nil , engine }
2014-06-17 23:42:34 +00:00
engine . router = httprouter . New ( )
engine . router . NotFound = engine . handle404
2014-07-06 19:09:23 +00:00
engine . cache . New = func ( ) interface { } {
return & Context { Engine : engine , Writer : & responseWriter { } }
2014-07-02 18:17:57 +00:00
}
2014-06-17 23:42:34 +00:00
return engine
}
// Returns a Engine instance with the Logger and Recovery already attached.
func Default ( ) * Engine {
engine := New ( )
engine . Use ( Recovery ( ) , Logger ( ) )
return engine
}
2014-07-15 15:41:56 +00:00
func ( engine * Engine ) LoadHTMLGlob ( pattern string ) {
templ := template . Must ( template . ParseGlob ( pattern ) )
engine . SetHTTPTemplate ( templ )
}
func ( engine * Engine ) LoadHTMLFiles ( files ... string ) {
templ := template . Must ( template . ParseFiles ( files ... ) )
engine . SetHTTPTemplate ( templ )
}
func ( engine * Engine ) SetHTTPTemplate ( templ * template . Template ) {
engine . HTMLRender = render . HTMLRender {
Template : templ ,
}
2014-06-17 23:42:34 +00:00
}
// Adds handlers for NotFound. It return a 404 code by default.
func ( engine * Engine ) NotFound404 ( handlers ... HandlerFunc ) {
engine . handlers404 = handlers
}
func ( engine * Engine ) handle404 ( w http . ResponseWriter , req * http . Request ) {
2014-06-30 01:59:00 +00:00
handlers := engine . combineHandlers ( engine . handlers404 )
2014-06-17 23:42:34 +00:00
c := engine . createContext ( w , req , nil , handlers )
2014-07-03 22:12:35 +00:00
c . Writer . setStatus ( 404 )
2014-06-30 01:59:00 +00:00
c . Next ( )
2014-07-03 22:12:35 +00:00
if ! c . Writer . Written ( ) {
2014-07-05 17:23:40 +00:00
c . Data ( 404 , MIMEPlain , [ ] byte ( "404 page not found" ) )
2014-07-03 22:12:35 +00:00
}
2014-07-06 19:09:23 +00:00
engine . cache . Put ( c )
2014-06-17 23:42:34 +00:00
}
// ServeHTTP makes the router implement the http.Handler interface.
func ( engine * Engine ) ServeHTTP ( w http . ResponseWriter , req * http . Request ) {
engine . router . ServeHTTP ( w , req )
}
func ( engine * Engine ) Run ( addr string ) {
2014-07-02 21:10:30 +00:00
if err := http . ListenAndServe ( addr , engine ) ; err != nil {
panic ( err )
}
2014-06-17 23:42:34 +00:00
}
2014-07-08 01:29:38 +00:00
func ( engine * Engine ) RunTLS ( addr string , cert string , key string ) {
if err := http . ListenAndServeTLS ( addr , cert , key , engine ) ; err != nil {
panic ( err )
}
}
2014-06-17 23:42:34 +00:00
/************************************/
/********** ROUTES GROUPING *********/
/************************************/
2014-07-02 18:17:57 +00:00
func ( engine * Engine ) createContext ( w http . ResponseWriter , req * http . Request , params httprouter . Params , handlers [ ] HandlerFunc ) * Context {
2014-07-06 19:09:23 +00:00
c := engine . cache . Get ( ) . ( * Context )
c . Writer . reset ( w )
2014-07-08 14:57:04 +00:00
c . Request = req
2014-07-06 19:09:23 +00:00
c . Params = params
c . handlers = handlers
c . Keys = nil
c . index = - 1
return c
2014-06-17 23:42:34 +00:00
}
// Adds middlewares to the group, see example code in github.
func ( group * RouterGroup ) Use ( middlewares ... HandlerFunc ) {
group . Handlers = append ( group . Handlers , middlewares ... )
}
2014-07-07 22:28:39 +00:00
func joinGroupPath ( elems ... string ) string {
joined := path . Join ( elems ... )
lastComponent := elems [ len ( elems ) - 1 ]
// Append a '/' if the last component had one, but only if it's not there already
if len ( lastComponent ) > 0 && lastComponent [ len ( lastComponent ) - 1 ] == '/' && joined [ len ( joined ) - 1 ] != '/' {
return joined + "/"
}
return joined
}
2014-07-03 18:30:01 +00:00
// Creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
2014-06-17 23:42:34 +00:00
// For example, all the routes that use a common middlware for authorization could be grouped.
func ( group * RouterGroup ) Group ( component string , handlers ... HandlerFunc ) * RouterGroup {
2014-07-07 22:28:39 +00:00
prefix := joinGroupPath ( group . prefix , component )
2014-06-17 23:42:34 +00:00
return & RouterGroup {
2014-06-30 01:59:00 +00:00
Handlers : group . combineHandlers ( handlers ) ,
2014-06-17 23:42:34 +00:00
parent : group ,
prefix : prefix ,
engine : group . engine ,
}
}
// Handle registers a new request handle and middlewares with the given path and method.
// The last handler should be the real handler, the other ones should be middlewares that can and should be shared among different routes.
// See the example code in github.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func ( group * RouterGroup ) Handle ( method , p string , handlers [ ] HandlerFunc ) {
2014-07-07 22:28:39 +00:00
p = joinGroupPath ( group . prefix , p )
2014-06-30 01:59:00 +00:00
handlers = group . combineHandlers ( handlers )
2014-06-17 23:42:34 +00:00
group . engine . router . Handle ( method , p , func ( w http . ResponseWriter , req * http . Request , params httprouter . Params ) {
2014-07-02 18:17:57 +00:00
c := group . engine . createContext ( w , req , params , handlers )
c . Next ( )
2014-07-06 19:09:23 +00:00
group . engine . cache . Put ( c )
2014-06-17 23:42:34 +00:00
} )
}
// POST is a shortcut for router.Handle("POST", path, handle)
func ( group * RouterGroup ) POST ( path string , handlers ... HandlerFunc ) {
group . Handle ( "POST" , path , handlers )
}
// GET is a shortcut for router.Handle("GET", path, handle)
func ( group * RouterGroup ) GET ( path string , handlers ... HandlerFunc ) {
group . Handle ( "GET" , path , handlers )
}
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func ( group * RouterGroup ) DELETE ( path string , handlers ... HandlerFunc ) {
group . Handle ( "DELETE" , path , handlers )
}
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func ( group * RouterGroup ) PATCH ( path string , handlers ... HandlerFunc ) {
group . Handle ( "PATCH" , path , handlers )
}
// PUT is a shortcut for router.Handle("PUT", path, handle)
func ( group * RouterGroup ) PUT ( path string , handlers ... HandlerFunc ) {
group . Handle ( "PUT" , path , handlers )
}
2014-07-03 14:14:33 +00:00
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
func ( group * RouterGroup ) OPTIONS ( path string , handlers ... HandlerFunc ) {
group . Handle ( "OPTIONS" , path , handlers )
2014-06-17 23:42:34 +00:00
}
2014-07-03 14:57:45 +00:00
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
func ( group * RouterGroup ) HEAD ( path string , handlers ... HandlerFunc ) {
group . Handle ( "HEAD" , path , handlers )
}
2014-07-07 23:09:48 +00:00
// Static serves files from the given file system root.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// To use the operating system's file system implementation,
// use :
// router.Static("/static", "/var/www")
func ( group * RouterGroup ) Static ( p , root string ) {
p = path . Join ( p , "/*filepath" )
fileServer := http . FileServer ( http . Dir ( root ) )
group . GET ( p , func ( c * Context ) {
2014-07-08 14:57:04 +00:00
original := c . Request . URL . Path
c . Request . URL . Path = c . Params . ByName ( "filepath" )
fileServer . ServeHTTP ( c . Writer , c . Request )
c . Request . URL . Path = original
2014-07-07 23:09:48 +00:00
} )
}
2014-06-30 01:59:00 +00:00
func ( group * RouterGroup ) combineHandlers ( handlers [ ] HandlerFunc ) [ ] HandlerFunc {
s := len ( group . Handlers ) + len ( handlers )
h := make ( [ ] HandlerFunc , 0 , s )
h = append ( h , group . Handlers ... )
h = append ( h , handlers ... )
return h
2014-06-17 23:42:34 +00:00
}
/************************************/
/****** FLOW AND ERROR MANAGEMENT****/
/************************************/
2014-07-03 14:31:27 +00:00
func ( c * Context ) Copy ( ) * Context {
2014-07-03 14:35:20 +00:00
var cp Context = * c
2014-07-03 14:31:27 +00:00
cp . index = AbortIndex
cp . handlers = nil
2014-07-03 14:35:20 +00:00
return & cp
2014-07-03 13:59:39 +00:00
}
2014-06-17 23:42:34 +00:00
// Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler.
// See example in github.
func ( c * Context ) Next ( ) {
c . index ++
s := int8 ( len ( c . handlers ) )
for ; c . index < s ; c . index ++ {
c . handlers [ c . index ] ( c )
}
}
// Forces the system to do not continue calling the pending handlers.
// For example, the first handler checks if the request is authorized. If it's not, context.Abort(401) should be called.
// The rest of pending handlers would never be called for that request.
func ( c * Context ) Abort ( code int ) {
2014-07-03 22:37:54 +00:00
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-17 23:42:34 +00:00
c . index = AbortIndex
}
2014-07-03 18:30:01 +00:00
// Fail is the same as Abort plus an error message.
2014-06-17 23:42:34 +00:00
// Calling `context.Fail(500, err)` is equivalent to:
// ```
// context.Error("Operation aborted", err)
// context.Abort(500)
// ```
func ( c * Context ) Fail ( code int , err error ) {
c . Error ( err , "Operation aborted" )
c . Abort ( code )
}
2014-07-07 22:16:41 +00:00
func ( c * Context ) ErrorTyped ( err error , typ uint32 , meta interface { } ) {
c . Errors = append ( c . Errors , errorMsg {
Err : err . Error ( ) ,
Type : typ ,
Meta : meta ,
} )
}
2014-07-03 18:30:01 +00:00
// Attaches an error to the current context. The error is pushed to a list of errors.
// It's a good idea to call Error for each error that occurred during the resolution of a request.
2014-06-17 23:42:34 +00:00
// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
func ( c * Context ) Error ( err error , meta interface { } ) {
2014-07-07 22:16:41 +00:00
c . ErrorTyped ( err , ErrorTypeExternal , meta )
2014-06-17 23:42:34 +00:00
}
2014-07-02 00:32:44 +00:00
func ( c * Context ) LastError ( ) error {
s := len ( c . Errors )
if s > 0 {
return errors . New ( c . Errors [ s - 1 ] . Err )
} else {
return nil
}
}
2014-06-17 23:42:34 +00:00
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
2014-07-03 18:30:01 +00:00
// Sets a new pair key/value just for the specified context.
// It also lazy initializes the hashmap.
2014-06-17 23:42:34 +00:00
func ( c * Context ) Set ( key string , item interface { } ) {
if c . Keys == nil {
c . Keys = make ( map [ string ] interface { } )
}
c . Keys [ key ] = item
}
2014-07-03 22:16:41 +00:00
// Get returns the value for the given key or an error if the key does not exist.
2014-07-03 19:17:24 +00:00
func ( c * Context ) Get ( key string ) ( interface { } , error ) {
2014-06-17 23:42:34 +00:00
if c . Keys != nil {
2014-07-03 19:17:24 +00:00
item , ok := c . Keys [ key ]
if ok {
return item , nil
}
2014-06-17 23:42:34 +00:00
}
2014-07-03 19:17:24 +00:00
return nil , errors . New ( "Key does not exist." )
2014-06-17 23:42:34 +00:00
}
2014-07-03 22:16:41 +00:00
// MustGet returns the value for the given key or panics if the value doesn't exist.
func ( c * Context ) MustGet ( key string ) interface { } {
value , err := c . Get ( key )
if err != nil || value == nil {
2014-06-17 23:42:34 +00:00
log . Panicf ( "Key %s doesn't exist" , key )
}
2014-07-03 22:16:41 +00:00
return value
2014-06-17 23:42:34 +00:00
}
/************************************/
/******** ENCOGING MANAGEMENT********/
/************************************/
2014-07-04 21:28:50 +00:00
func filterFlags ( content string ) string {
for i , a := range content {
if a == ' ' || a == ';' {
return content [ : i ]
}
}
return content
2014-07-03 17:19:06 +00:00
}
// This function checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding
// "application/xml" --> XML binding
// else --> returns an error
// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
func ( c * Context ) Bind ( obj interface { } ) bool {
2014-07-04 21:28:50 +00:00
var b binding . Binding
2014-07-08 14:57:04 +00:00
ctype := filterFlags ( c . Request . Header . Get ( "Content-Type" ) )
2014-07-04 21:28:50 +00:00
switch {
2014-07-08 14:57:04 +00:00
case c . Request . Method == "GET" || ctype == MIMEPOSTForm :
2014-07-04 21:28:50 +00:00
b = binding . Form
case ctype == MIMEJSON :
b = binding . JSON
case ctype == MIMEXML || ctype == MIMEXML2 :
b = binding . XML
2014-07-03 17:19:06 +00:00
default :
2014-07-04 21:28:50 +00:00
c . Fail ( 400 , errors . New ( "unknown content-type: " + ctype ) )
2014-06-17 23:42:34 +00:00
return false
}
2014-07-04 21:28:50 +00:00
return c . BindWith ( obj , b )
2014-06-17 23:42:34 +00:00
}
2014-07-03 17:19:06 +00:00
func ( c * Context ) BindWith ( obj interface { } , b binding . Binding ) bool {
2014-07-08 14:57:04 +00:00
if err := b . Bind ( c . Request , obj ) ; err != nil {
2014-07-03 17:19:06 +00:00
c . Fail ( 400 , err )
return false
2014-06-17 23:42:34 +00:00
}
2014-07-03 17:19:06 +00:00
return true
2014-06-17 23:42:34 +00:00
}
2014-07-15 15:41:56 +00:00
func ( c * Context ) Render ( code int , render render . Render , obj ... interface { } ) {
if err := render . Render ( c . Writer , code , obj ) ; err != nil {
2014-07-07 22:16:41 +00:00
c . ErrorTyped ( err , ErrorTypeInternal , obj )
c . Abort ( 500 )
2014-06-17 23:42:34 +00:00
}
}
2014-07-15 15:41:56 +00:00
// Serializes the given struct as JSON into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/json".
func ( c * Context ) JSON ( code int , obj interface { } ) {
c . Render ( code , render . JSON , obj )
}
2014-07-03 18:30:01 +00:00
// Serializes the given struct as XML into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/xml".
2014-06-17 23:42:34 +00:00
func ( c * Context ) XML ( code int , obj interface { } ) {
2014-07-15 15:41:56 +00:00
c . Render ( code , render . XML , obj )
2014-06-17 23:42:34 +00:00
}
2014-07-03 18:30:01 +00:00
// Renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
2014-06-17 23:42:34 +00:00
// See http://golang.org/doc/articles/wiki/
2014-07-15 15:41:56 +00:00
func ( c * Context ) HTML ( code int , name string , obj interface { } ) {
c . Render ( code , c . Engine . HTMLRender , name , obj )
2014-06-17 23:42:34 +00:00
}
2014-07-03 18:30:01 +00:00
// Writes the given string into the response body and sets the Content-Type to "text/plain".
2014-07-05 14:14:43 +00:00
func ( c * Context ) String ( code int , format string , values ... interface { } ) {
2014-07-15 15:41:56 +00:00
c . Render ( code , render . Plain , format , values )
2014-06-17 23:42:34 +00:00
}
2014-07-03 18:30:01 +00:00
// Writes some data into the body stream and updates the HTTP code.
2014-07-05 00:53:51 +00:00
func ( c * Context ) Data ( code int , contentType string , data [ ] byte ) {
if len ( contentType ) > 0 {
c . Writer . Header ( ) . Set ( "Content-Type" , contentType )
}
if code >= 0 {
c . Writer . WriteHeader ( code )
}
2014-06-17 23:42:34 +00:00
c . Writer . Write ( data )
}