2014-08-29 21:49:50 +04:00
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
2014-07-16 22:14:03 +04:00
package gin
import (
"errors"
2015-03-26 16:07:01 +03:00
"math"
2014-07-16 22:14:03 +04:00
"net/http"
2014-12-21 15:42:48 +03:00
"strings"
2015-03-23 06:39:53 +03:00
"github.com/gin-gonic/gin/binding"
"github.com/gin-gonic/gin/render"
2015-05-05 16:06:38 +03:00
"golang.org/x/net/context"
2014-07-16 22:14:03 +04:00
)
2015-04-08 15:24:49 +03:00
const (
MIMEJSON = binding . MIMEJSON
MIMEHTML = binding . MIMEHTML
MIMEXML = binding . MIMEXML
MIMEXML2 = binding . MIMEXML2
MIMEPlain = binding . MIMEPlain
MIMEPOSTForm = binding . MIMEPOSTForm
MIMEMultipartPOSTForm = binding . MIMEMultipartPOSTForm
)
2015-03-26 06:27:34 +03:00
const AbortIndex = math . MaxInt8 / 2
2014-07-16 22:14:03 +04:00
2015-05-05 16:06:38 +03:00
// Param is a single URL parameter, consisting of a key and a value.
type Param struct {
Key string
Value string
}
// Params is a Param-slice, as returned by the router.
// The slice is ordered, the first URL parameter is also the first slice value.
// It is therefore safe to read values by the index.
type Params [ ] Param
// ByName returns the value of the first Param which key matches the given name.
// If no matching Param is found, an empty string is returned.
func ( ps Params ) ByName ( name string ) string {
for _ , entry := range ps {
if entry . Key == name {
return entry . Value
}
}
return ""
}
2014-07-16 22:14:03 +04: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.
type Context struct {
2015-05-05 16:06:38 +03:00
context . Context
2014-07-18 02:10:28 +04:00
writermem responseWriter
Request * http . Request
Writer ResponseWriter
2015-03-31 18:39:30 +03:00
2015-03-31 22:39:06 +03:00
Params Params
2015-05-07 12:30:01 +03:00
handlers HandlersChain
2015-03-31 18:39:30 +03:00
index int8
2015-04-07 13:22:38 +03:00
Engine * Engine
2015-03-31 18:39:30 +03:00
Keys map [ string ] interface { }
Errors errorMsgs
2015-04-08 00:28:36 +03:00
Accepted [ ] string
2014-07-16 22:14:03 +04:00
}
/************************************/
2014-10-08 23:37:26 +04:00
/********** CONTEXT CREATION ********/
2014-07-16 22:14:03 +04:00
/************************************/
2015-03-25 21:33:17 +03:00
func ( c * Context ) reset ( ) {
2015-04-07 13:22:38 +03:00
c . Writer = & c . writermem
c . Params = c . Params [ 0 : 0 ]
c . handlers = nil
2015-03-25 21:33:17 +03:00
c . index = - 1
2015-04-07 13:22:38 +03:00
c . Keys = nil
2015-03-25 21:33:17 +03:00
c . Errors = c . Errors [ 0 : 0 ]
2015-04-08 14:37:25 +03:00
c . Accepted = nil
2015-03-25 21:33:17 +03:00
}
2014-07-16 22:14:03 +04:00
func ( c * Context ) Copy ( ) * Context {
var cp Context = * c
2015-04-09 13:15:02 +03:00
cp . writermem . ResponseWriter = nil
cp . Writer = & cp . writermem
2014-07-16 22:14:03 +04:00
cp . index = AbortIndex
cp . handlers = nil
return & cp
}
2014-10-08 23:37:26 +04:00
/************************************/
/*************** FLOW ***************/
/************************************/
2014-07-16 22:14:03 +04: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 )
}
}
2015-03-17 21:51:03 +03:00
// Forces the system to not continue calling the pending handlers in the chain.
2014-10-08 23:37:26 +04:00
func ( c * Context ) Abort ( ) {
2014-07-16 22:14:03 +04:00
c . index = AbortIndex
}
2014-10-08 23:37:26 +04:00
// Same than AbortWithStatus() but also writes the specified response status code.
// For example, the first handler checks if the request is authorized. If it's not, context.AbortWithStatus(401) should be called.
func ( c * Context ) AbortWithStatus ( code int ) {
c . Writer . WriteHeader ( code )
c . Abort ( )
}
2015-04-08 03:58:35 +03:00
func ( c * Context ) IsAborted ( ) bool {
return c . index == AbortIndex
}
2014-10-08 23:37:26 +04:00
/************************************/
/********* ERROR MANAGEMENT *********/
/************************************/
2014-07-16 22:14:03 +04:00
// Fail is the same as Abort plus an error message.
// Calling `context.Fail(500, err)` is equivalent to:
// ```
// context.Error("Operation aborted", err)
2014-10-08 23:37:26 +04:00
// context.AbortWithStatus(500)
2014-07-16 22:14:03 +04:00
// ```
func ( c * Context ) Fail ( code int , err error ) {
c . Error ( err , "Operation aborted" )
2014-10-08 23:37:26 +04:00
c . AbortWithStatus ( code )
2014-07-16 22:14:03 +04:00
}
2015-04-08 03:58:35 +03:00
func ( c * Context ) ErrorTyped ( err error , typ int , meta interface { } ) {
2014-07-16 22:14:03 +04:00
c . Errors = append ( c . Errors , errorMsg {
Err : err . Error ( ) ,
Type : typ ,
Meta : meta ,
} )
}
// 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.
// 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 { } ) {
c . ErrorTyped ( err , ErrorTypeExternal , meta )
}
func ( c * Context ) LastError ( ) error {
2014-10-08 23:37:26 +04:00
nuErrors := len ( c . Errors )
if nuErrors > 0 {
return errors . New ( c . Errors [ nuErrors - 1 ] . Err )
2014-07-16 22:14:03 +04:00
}
2015-04-07 13:22:38 +03:00
return nil
2014-07-16 22:14:03 +04:00
}
2015-05-05 16:06:38 +03:00
/************************************/
/************ INPUT DATA ************/
/************************************/
/** Shortcut for c.Request.FormValue(key) */
func ( c * Context ) FormValue ( key string ) ( va string ) {
va , _ = c . formValue ( key )
return
}
/** Shortcut for c.Request.PostFormValue(key) */
func ( c * Context ) PostFormValue ( key string ) ( va string ) {
va , _ = c . postFormValue ( key )
return
}
/** Shortcut for c.Params.ByName(key) */
func ( c * Context ) ParamValue ( key string ) ( va string ) {
va , _ = c . paramValue ( key )
return
}
func ( c * Context ) DefaultPostFormValue ( key , defaultValue string ) string {
if va , ok := c . postFormValue ( key ) ; ok {
return va
} else {
return defaultValue
}
}
func ( c * Context ) DefaultFormValue ( key , defaultValue string ) string {
if va , ok := c . formValue ( key ) ; ok {
return va
} else {
return defaultValue
}
}
func ( c * Context ) DefaultParamValue ( key , defaultValue string ) string {
if va , ok := c . paramValue ( key ) ; ok {
return va
} else {
return defaultValue
}
}
func ( c * Context ) paramValue ( key string ) ( string , bool ) {
va := c . Params . ByName ( key )
return va , len ( va ) > 0
}
func ( c * Context ) formValue ( key string ) ( string , bool ) {
req := c . Request
req . ParseForm ( )
if values , ok := req . Form [ key ] ; ok && len ( values ) > 0 {
return values [ 0 ] , true
}
return "" , false
}
func ( c * Context ) postFormValue ( key string ) ( string , bool ) {
req := c . Request
req . ParseForm ( )
if values , ok := req . PostForm [ key ] ; ok && len ( values ) > 0 {
return values [ 0 ] , true
}
return "" , false
}
2014-07-16 22:14:03 +04:00
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
// Sets a new pair key/value just for the specified context.
// It also lazy initializes the hashmap.
func ( c * Context ) Set ( key string , item interface { } ) {
if c . Keys == nil {
c . Keys = make ( map [ string ] interface { } )
}
c . Keys [ key ] = item
}
// Get returns the value for the given key or an error if the key does not exist.
2015-03-31 18:44:45 +03:00
func ( c * Context ) Get ( key string ) ( value interface { } , ok bool ) {
2014-07-16 22:14:03 +04:00
if c . Keys != nil {
2015-03-31 18:44:45 +03:00
value , ok = c . Keys [ key ]
2014-07-16 22:14:03 +04:00
}
2015-03-31 18:44:45 +03:00
return
2014-07-16 22:14:03 +04:00
}
// MustGet returns the value for the given key or panics if the value doesn't exist.
func ( c * Context ) MustGet ( key string ) interface { } {
2015-03-31 18:44:45 +03:00
if value , exists := c . Get ( key ) ; exists {
return value
} else {
2015-04-09 13:15:02 +03:00
panic ( "Key \"" + key + "\" does not exist" )
2014-07-16 22:14:03 +04:00
}
}
2015-05-05 16:06:38 +03:00
func ( c * Context ) Value ( key interface { } ) interface { } {
if key == 0 {
return c . Request
}
if keyAsString , ok := key . ( string ) ; ok {
val , _ := c . Get ( keyAsString )
return val
}
return c . Context . Value ( key )
}
2015-03-31 18:44:45 +03:00
/************************************/
/********* PARSING REQUEST **********/
/************************************/
2014-12-21 15:42:48 +03:00
2015-03-31 18:44:45 +03:00
func ( c * Context ) ClientIP ( ) string {
clientIP := c . Request . Header . Get ( "X-Real-IP" )
if len ( clientIP ) > 0 {
return clientIP
2014-12-21 15:42:48 +03:00
}
2015-03-31 18:44:45 +03:00
clientIP = c . Request . Header . Get ( "X-Forwarded-For" )
clientIP = strings . Split ( clientIP , "," ) [ 0 ]
if len ( clientIP ) > 0 {
2015-04-08 03:58:35 +03:00
return strings . TrimSpace ( clientIP )
2014-10-09 03:40:42 +04:00
}
2014-12-21 15:42:48 +03:00
return c . Request . RemoteAddr
2014-10-09 03:40:42 +04:00
}
2015-03-31 18:44:45 +03:00
func ( c * Context ) ContentType ( ) string {
return filterFlags ( c . Request . Header . Get ( "Content-Type" ) )
}
2014-07-16 22:14:03 +04: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 {
2015-03-31 18:51:10 +03:00
b := binding . Default ( c . Request . Method , c . ContentType ( ) )
2014-07-16 22:14:03 +04:00
return c . BindWith ( obj , b )
}
func ( c * Context ) BindWith ( obj interface { } , b binding . Binding ) bool {
if err := b . Bind ( c . Request , obj ) ; err != nil {
c . Fail ( 400 , err )
return false
}
return true
}
2014-10-08 23:37:26 +04:00
/************************************/
/******** RESPONSE RENDERING ********/
/************************************/
2015-05-07 13:44:52 +03:00
func ( c * Context ) renderingError ( err error , meta ... interface { } ) {
c . ErrorTyped ( err , ErrorTypeInternal , meta )
c . AbortWithStatus ( 500 )
}
2014-07-16 22:14:03 +04:00
func ( c * Context ) Render ( code int , render render . Render , obj ... interface { } ) {
if err := render . Render ( c . Writer , code , obj ... ) ; err != nil {
2015-05-07 13:44:52 +03:00
c . renderingError ( err , obj )
2014-07-16 22:14:03 +04:00
}
}
2015-05-07 13:44:52 +03:00
// Renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
func ( c * Context ) HTML ( code int , name string , obj interface { } ) {
c . Render ( code , c . Engine . HTMLRender , name , obj )
2015-05-06 23:31:01 +03:00
}
func ( c * Context ) IndentedJSON ( code int , obj interface { } ) {
c . Render ( code , render . IndentedJSON , obj )
2014-07-16 22:14:03 +04:00
}
2015-05-07 13:44:52 +03: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 { } ) {
if err := render . WriteJSON ( c . Writer , code , obj ) ; err != nil {
c . renderingError ( err , obj )
}
}
2014-07-16 22:14:03 +04: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".
func ( c * Context ) XML ( code int , obj interface { } ) {
2015-05-07 13:44:52 +03:00
if err := render . WriteXML ( c . Writer , code , obj ) ; err != nil {
c . renderingError ( err , obj )
}
2014-07-16 22:14:03 +04:00
}
// Writes the given string into the response body and sets the Content-Type to "text/plain".
func ( c * Context ) String ( code int , format string , values ... interface { } ) {
2015-05-07 13:44:52 +03:00
render . WritePlainText ( c . Writer , code , format , values )
2014-07-16 22:14:03 +04:00
}
2015-03-08 19:50:58 +03:00
// Writes the given string into the response body and sets the Content-Type to "text/html" without template.
func ( c * Context ) HTMLString ( code int , format string , values ... interface { } ) {
2015-05-07 13:44:52 +03:00
render . WriteHTMLString ( c . Writer , code , format , values )
2015-03-08 19:50:58 +03:00
}
2014-07-29 02:51:34 +04:00
// Returns a HTTP redirect to the specific location.
2014-08-02 19:06:09 +04:00
func ( c * Context ) Redirect ( code int , location string ) {
2015-05-07 13:44:52 +03:00
render . WriteRedirect ( c . Writer , code , c . Request , location )
2014-07-29 02:48:02 +04:00
}
2014-07-16 22:14:03 +04:00
// Writes some data into the body stream and updates the HTTP code.
func ( c * Context ) Data ( code int , contentType string , data [ ] byte ) {
2015-05-07 13:44:52 +03:00
render . WriteData ( c . Writer , code , contentType , data )
2014-07-16 22:14:03 +04:00
}
2014-07-17 04:01:42 +04:00
// Writes the specified file into the body stream
func ( c * Context ) File ( filepath string ) {
http . ServeFile ( c . Writer , c . Request , filepath )
}
2014-08-31 00:22:57 +04:00
/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/
2014-08-31 20:28:18 +04:00
2014-08-31 00:22:57 +04:00
type Negotiate struct {
Offered [ ] string
HTMLPath string
2014-08-31 20:28:18 +04:00
HTMLData interface { }
JSONData interface { }
XMLData interface { }
Data interface { }
2014-08-31 00:22:57 +04:00
}
2014-08-31 20:28:18 +04:00
func ( c * Context ) Negotiate ( code int , config Negotiate ) {
2014-08-31 20:41:11 +04:00
switch c . NegotiateFormat ( config . Offered ... ) {
2015-03-31 18:51:10 +03:00
case binding . MIMEJSON :
2014-08-31 20:28:18 +04:00
data := chooseData ( config . JSONData , config . Data )
2014-08-31 00:22:57 +04:00
c . JSON ( code , data )
2015-03-31 18:51:10 +03:00
case binding . MIMEHTML :
2014-08-31 20:28:18 +04:00
if len ( config . HTMLPath ) == 0 {
2015-04-08 03:58:35 +03:00
panic ( "negotiate config is wrong. html path is needed" )
2014-08-31 20:28:18 +04:00
}
2015-03-31 18:51:10 +03:00
data := chooseData ( config . HTMLData , config . Data )
2014-08-31 20:28:18 +04:00
c . HTML ( code , config . HTMLPath , data )
2014-08-31 00:22:57 +04:00
2015-03-31 18:51:10 +03:00
case binding . MIMEXML :
2014-08-31 20:28:18 +04:00
data := chooseData ( config . XMLData , config . Data )
2014-08-31 00:22:57 +04:00
c . XML ( code , data )
2014-08-31 20:28:18 +04:00
2014-08-31 00:22:57 +04:00
default :
2014-08-31 20:41:11 +04:00
c . Fail ( http . StatusNotAcceptable , errors . New ( "the accepted formats are not offered by the server" ) )
2014-08-31 00:22:57 +04:00
}
}
func ( c * Context ) NegotiateFormat ( offered ... string ) string {
2014-08-31 20:41:11 +04:00
if len ( offered ) == 0 {
2015-04-08 03:58:35 +03:00
panic ( "you must provide at least one offer" )
2014-08-31 20:41:11 +04:00
}
2015-04-08 00:28:36 +03:00
if c . Accepted == nil {
c . Accepted = parseAccept ( c . Request . Header . Get ( "Accept" ) )
2014-08-31 00:22:57 +04:00
}
2015-04-08 00:28:36 +03:00
if len ( c . Accepted ) == 0 {
2014-08-31 00:22:57 +04:00
return offered [ 0 ]
2015-04-08 00:28:36 +03:00
}
for _ , accepted := range c . Accepted {
for _ , offert := range offered {
if accepted == offert {
return offert
2014-08-31 00:22:57 +04:00
}
}
}
2015-04-08 00:28:36 +03:00
return ""
2014-08-31 00:22:57 +04:00
}
func ( c * Context ) SetAccepted ( formats ... string ) {
2015-04-08 00:28:36 +03:00
c . Accepted = formats
2014-08-31 00:22:57 +04:00
}