- More unit tests
- Improves HTML debug render - InputHolder removed - More debug logs
This commit is contained in:
		| @ -4,8 +4,7 @@ List of all the awesome people working to make Gin the best Web Framework in Go. | |||||||
|  |  | ||||||
| ##gin 0.x series authors | ##gin 0.x series authors | ||||||
|  |  | ||||||
| **Original Developer:**  Manu Martinez-Almeida (@manucorporat)   | **Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho) | ||||||
| **Long-term Maintainer:** Javier Provecho (@javierprovecho) |  | ||||||
|  |  | ||||||
| People and companies, who have contributed, in alphabetical order. | People and companies, who have contributed, in alphabetical order. | ||||||
|  |  | ||||||
|  | |||||||
| @ -28,25 +28,22 @@ type Binding interface { | |||||||
| var _validator = validator.NewValidator("binding", validator.BakedInValidators) | var _validator = validator.NewValidator("binding", validator.BakedInValidators) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	JSON     = jsonBinding{} | 	JSON = jsonBinding{} | ||||||
| 	XML      = xmlBinding{} | 	XML  = xmlBinding{} | ||||||
| 	GETForm  = getFormBinding{} | 	Form = formBinding{} | ||||||
| 	POSTForm = postFormBinding{} |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Default(method, contentType string) Binding { | func Default(method, contentType string) Binding { | ||||||
| 	if method == "GET" { | 	if method == "GET" { | ||||||
| 		return GETForm | 		return Form | ||||||
| 	} else { | 	} else { | ||||||
| 		switch contentType { | 		switch contentType { | ||||||
| 		case MIMEPOSTForm: |  | ||||||
| 			return POSTForm |  | ||||||
| 		case MIMEJSON: | 		case MIMEJSON: | ||||||
| 			return JSON | 			return JSON | ||||||
| 		case MIMEXML, MIMEXML2: | 		case MIMEXML, MIMEXML2: | ||||||
| 			return XML | 			return XML | ||||||
| 		default: | 		default: | ||||||
| 			return GETForm | 			return Form | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,8 +17,8 @@ type FooStruct struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingDefault(t *testing.T) { | func TestBindingDefault(t *testing.T) { | ||||||
| 	assert.Equal(t, Default("GET", ""), GETForm) | 	assert.Equal(t, Default("GET", ""), Form) | ||||||
| 	assert.Equal(t, Default("GET", MIMEJSON), GETForm) | 	assert.Equal(t, Default("GET", MIMEJSON), Form) | ||||||
|  |  | ||||||
| 	assert.Equal(t, Default("POST", MIMEJSON), JSON) | 	assert.Equal(t, Default("POST", MIMEJSON), JSON) | ||||||
| 	assert.Equal(t, Default("PUT", MIMEJSON), JSON) | 	assert.Equal(t, Default("PUT", MIMEJSON), JSON) | ||||||
| @ -26,54 +26,71 @@ func TestBindingDefault(t *testing.T) { | |||||||
| 	assert.Equal(t, Default("POST", MIMEXML), XML) | 	assert.Equal(t, Default("POST", MIMEXML), XML) | ||||||
| 	assert.Equal(t, Default("PUT", MIMEXML2), XML) | 	assert.Equal(t, Default("PUT", MIMEXML2), XML) | ||||||
|  |  | ||||||
| 	assert.Equal(t, Default("POST", MIMEPOSTForm), POSTForm) | 	assert.Equal(t, Default("POST", MIMEPOSTForm), Form) | ||||||
| 	assert.Equal(t, Default("DELETE", MIMEPOSTForm), POSTForm) | 	assert.Equal(t, Default("DELETE", MIMEPOSTForm), Form) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingJSON(t *testing.T) { | func TestBindingJSON(t *testing.T) { | ||||||
| 	testBinding(t, | 	testBodyBinding(t, | ||||||
| 		JSON, "json", | 		JSON, "json", | ||||||
| 		"/", "/", | 		"/", "/", | ||||||
| 		`{"foo": "bar"}`, `{"bar": "foo"}`) | 		`{"foo": "bar"}`, `{"bar": "foo"}`) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingPOSTForm(t *testing.T) { | func TestBindingForm(t *testing.T) { | ||||||
| 	testBinding(t, | 	testFormBinding(t, "POST", | ||||||
| 		POSTForm, "post_form", |  | ||||||
| 		"/", "/", | 		"/", "/", | ||||||
| 		"foo=bar", "bar=foo") | 		"foo=bar", "bar=foo") | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingGETForm(t *testing.T) { | func TestBindingForm2(t *testing.T) { | ||||||
| 	testBinding(t, | 	testFormBinding(t, "GET", | ||||||
| 		GETForm, "get_form", |  | ||||||
| 		"/?foo=bar", "/?bar=foo", | 		"/?foo=bar", "/?bar=foo", | ||||||
| 		"", "") | 		"", "") | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingXML(t *testing.T) { | func TestBindingXML(t *testing.T) { | ||||||
| 	testBinding(t, | 	testBodyBinding(t, | ||||||
| 		XML, "xml", | 		XML, "xml", | ||||||
| 		"/", "/", | 		"/", "/", | ||||||
| 		"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>") | 		"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>") | ||||||
| } | } | ||||||
|  |  | ||||||
| func testBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { | func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { | ||||||
| 	assert.Equal(t, b.Name(), name) | 	b := Form | ||||||
|  | 	assert.Equal(t, b.Name(), "query") | ||||||
|  |  | ||||||
| 	obj := FooStruct{} | 	obj := FooStruct{} | ||||||
| 	req := requestWithBody(path, body) | 	req := requestWithBody(method, path, body) | ||||||
|  | 	if method == "POST" { | ||||||
|  | 		req.Header.Add("Content-Type", MIMEPOSTForm) | ||||||
|  | 	} | ||||||
| 	err := b.Bind(req, &obj) | 	err := b.Bind(req, &obj) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, obj.Foo, "bar") | 	assert.Equal(t, obj.Foo, "bar") | ||||||
|  |  | ||||||
| 	obj = FooStruct{} | 	obj = FooStruct{} | ||||||
| 	req = requestWithBody(badPath, badBody) | 	req = requestWithBody(method, badPath, badBody) | ||||||
| 	err = JSON.Bind(req, &obj) | 	err = JSON.Bind(req, &obj) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| } | } | ||||||
|  |  | ||||||
| func requestWithBody(path, body string) (req *http.Request) { | func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { | ||||||
| 	req, _ = http.NewRequest("POST", path, bytes.NewBufferString(body)) | 	assert.Equal(t, b.Name(), name) | ||||||
|  |  | ||||||
|  | 	obj := FooStruct{} | ||||||
|  | 	req := requestWithBody("POST", path, body) | ||||||
|  | 	err := b.Bind(req, &obj) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, obj.Foo, "bar") | ||||||
|  |  | ||||||
|  | 	obj = FooStruct{} | ||||||
|  | 	req = requestWithBody("POST", badPath, badBody) | ||||||
|  | 	err = JSON.Bind(req, &obj) | ||||||
|  | 	assert.Error(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func requestWithBody(method, path, body string) (req *http.Request) { | ||||||
|  | 	req, _ = http.NewRequest(method, path, bytes.NewBufferString(body)) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,13 +6,13 @@ package binding | |||||||
| 
 | 
 | ||||||
| import "net/http" | import "net/http" | ||||||
| 
 | 
 | ||||||
| type getFormBinding struct{} | type formBinding struct{} | ||||||
| 
 | 
 | ||||||
| func (_ getFormBinding) Name() string { | func (_ formBinding) Name() string { | ||||||
| 	return "get_form" | 	return "query" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (_ getFormBinding) Bind(req *http.Request, obj interface{}) error { | func (_ formBinding) Bind(req *http.Request, obj interface{}) error { | ||||||
| 	if err := req.ParseForm(); err != nil { | 	if err := req.ParseForm(); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -1,23 +0,0 @@ | |||||||
| // 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. |  | ||||||
|  |  | ||||||
| package binding |  | ||||||
|  |  | ||||||
| import "net/http" |  | ||||||
|  |  | ||||||
| type postFormBinding struct{} |  | ||||||
|  |  | ||||||
| func (_ postFormBinding) Name() string { |  | ||||||
| 	return "post_form" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (_ postFormBinding) Bind(req *http.Request, obj interface{}) error { |  | ||||||
| 	if err := req.ParseForm(); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if err := mapForm(obj, req.PostForm); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return Validate(obj) |  | ||||||
| } |  | ||||||
							
								
								
									
										106
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								context.go
									
									
									
									
									
								
							| @ -13,6 +13,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/gin-gonic/gin/binding" | 	"github.com/gin-gonic/gin/binding" | ||||||
| 	"github.com/gin-gonic/gin/render" | 	"github.com/gin-gonic/gin/render" | ||||||
|  | 	"golang.org/x/net/context" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @ -27,15 +28,37 @@ const ( | |||||||
|  |  | ||||||
| const AbortIndex = math.MaxInt8 / 2 | const AbortIndex = math.MaxInt8 / 2 | ||||||
|  |  | ||||||
|  | // 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 "" | ||||||
|  | } | ||||||
|  |  | ||||||
| // Context is the most important part of gin. It allows us to pass variables between middleware, | // 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. | // manage the flow, validate the JSON of a request and render a JSON response for example. | ||||||
| type Context struct { | type Context struct { | ||||||
|  | 	context.Context | ||||||
| 	writermem responseWriter | 	writermem responseWriter | ||||||
| 	Request   *http.Request | 	Request   *http.Request | ||||||
| 	Writer    ResponseWriter | 	Writer    ResponseWriter | ||||||
|  |  | ||||||
| 	Params   Params | 	Params   Params | ||||||
| 	Input    inputHolder |  | ||||||
| 	handlers []HandlerFunc | 	handlers []HandlerFunc | ||||||
| 	index    int8 | 	index    int8 | ||||||
|  |  | ||||||
| @ -63,7 +86,6 @@ func (c *Context) Copy() *Context { | |||||||
| 	var cp Context = *c | 	var cp Context = *c | ||||||
| 	cp.writermem.ResponseWriter = nil | 	cp.writermem.ResponseWriter = nil | ||||||
| 	cp.Writer = &cp.writermem | 	cp.Writer = &cp.writermem | ||||||
| 	cp.Input.context = &cp |  | ||||||
| 	cp.index = AbortIndex | 	cp.index = AbortIndex | ||||||
| 	cp.handlers = nil | 	cp.handlers = nil | ||||||
| 	return &cp | 	return &cp | ||||||
| @ -138,6 +160,75 @@ func (c *Context) LastError() error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /************************************/ | ||||||
|  | /************ 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 | ||||||
|  | } | ||||||
|  |  | ||||||
| /************************************/ | /************************************/ | ||||||
| /******** METADATA MANAGEMENT********/ | /******** METADATA MANAGEMENT********/ | ||||||
| /************************************/ | /************************************/ | ||||||
| @ -168,6 +259,17 @@ func (c *Context) MustGet(key string) interface{} { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  | } | ||||||
|  |  | ||||||
| /************************************/ | /************************************/ | ||||||
| /********* PARSING REQUEST **********/ | /********* PARSING REQUEST **********/ | ||||||
| /************************************/ | /************************************/ | ||||||
|  | |||||||
| @ -79,15 +79,58 @@ func TestContextCopy(t *testing.T) { | |||||||
|  |  | ||||||
| 	cp := c.Copy() | 	cp := c.Copy() | ||||||
| 	assert.Nil(t, cp.handlers) | 	assert.Nil(t, cp.handlers) | ||||||
|  | 	assert.Nil(t, cp.writermem.ResponseWriter) | ||||||
|  | 	assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) | ||||||
| 	assert.Equal(t, cp.Request, c.Request) | 	assert.Equal(t, cp.Request, c.Request) | ||||||
| 	assert.Equal(t, cp.index, AbortIndex) | 	assert.Equal(t, cp.index, AbortIndex) | ||||||
| 	assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) |  | ||||||
| 	assert.Equal(t, cp.Input.context, cp) |  | ||||||
| 	assert.Equal(t, cp.Keys, c.Keys) | 	assert.Equal(t, cp.Keys, c.Keys) | ||||||
| 	assert.Equal(t, cp.Engine, c.Engine) | 	assert.Equal(t, cp.Engine, c.Engine) | ||||||
| 	assert.Equal(t, cp.Params, c.Params) | 	assert.Equal(t, cp.Params, c.Params) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestContextFormParse(t *testing.T) { | ||||||
|  | 	c, _, _ := createTestContext() | ||||||
|  | 	c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10", nil) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultFormValue("foo", "none"), "bar") | ||||||
|  | 	assert.Equal(t, c.FormValue("foo"), "bar") | ||||||
|  | 	assert.Empty(t, c.PostFormValue("foo")) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultFormValue("page", "0"), "10") | ||||||
|  | 	assert.Equal(t, c.FormValue("page"), "10") | ||||||
|  | 	assert.Empty(t, c.PostFormValue("page")) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultFormValue("NoKey", "nada"), "nada") | ||||||
|  | 	assert.Empty(t, c.FormValue("NoKey")) | ||||||
|  | 	assert.Empty(t, c.PostFormValue("NoKey")) | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestContextPostFormParse(t *testing.T) { | ||||||
|  | 	c, _, _ := createTestContext() | ||||||
|  | 	body := bytes.NewBufferString("foo=bar&page=11&both=POST") | ||||||
|  | 	c.Request, _ = http.NewRequest("POST", "http://example.com/?both=GET&id=main", body) | ||||||
|  | 	c.Request.Header.Add("Content-Type", MIMEPOSTForm) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultPostFormValue("foo", "none"), "bar") | ||||||
|  | 	assert.Equal(t, c.PostFormValue("foo"), "bar") | ||||||
|  | 	assert.Equal(t, c.FormValue("foo"), "bar") | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultPostFormValue("page", "0"), "11") | ||||||
|  | 	assert.Equal(t, c.PostFormValue("page"), "11") | ||||||
|  | 	assert.Equal(t, c.FormValue("page"), "11") | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.PostFormValue("both"), "POST") | ||||||
|  | 	assert.Equal(t, c.FormValue("both"), "POST") | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.FormValue("id"), "main") | ||||||
|  | 	assert.Empty(t, c.PostFormValue("id")) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, c.DefaultPostFormValue("NoKey", "nada"), "nada") | ||||||
|  | 	assert.Empty(t, c.PostFormValue("NoKey")) | ||||||
|  | 	assert.Empty(t, c.FormValue("NoKey")) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Tests that the response is serialized as JSON | // Tests that the response is serialized as JSON | ||||||
| // and Content-Type is set to application/json | // and Content-Type is set to application/json | ||||||
| func TestContextRenderJSON(t *testing.T) { | func TestContextRenderJSON(t *testing.T) { | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								gin.go
									
									
									
									
									
								
							| @ -86,9 +86,7 @@ func Default() *Engine { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) allocateContext() (context *Context) { | func (engine *Engine) allocateContext() (context *Context) { | ||||||
| 	context = &Context{Engine: engine} | 	return &Context{Engine: engine} | ||||||
| 	context.Input = inputHolder{context: context} |  | ||||||
| 	return |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) LoadHTMLGlob(pattern string) { | func (engine *Engine) LoadHTMLGlob(pattern string) { | ||||||
| @ -110,9 +108,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) SetHTMLTemplate(templ *template.Template) { | func (engine *Engine) SetHTMLTemplate(templ *template.Template) { | ||||||
| 	engine.HTMLRender = render.HTMLRender{ | 	engine.HTMLRender = render.HTMLRender{Template: templ} | ||||||
| 		Template: templ, |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Adds handlers for NoRoute. It return a 404 code by default. | // Adds handlers for NoRoute. It return a 404 code by default. | ||||||
| @ -160,11 +156,13 @@ func (engine *Engine) handle(method, path string, handlers []HandlerFunc) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) Run(addr string) error { | func (engine *Engine) Run(addr string) error { | ||||||
|  | 	debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production") | ||||||
| 	debugPrint("Listening and serving HTTP on %s\n", addr) | 	debugPrint("Listening and serving HTTP on %s\n", addr) | ||||||
| 	return http.ListenAndServe(addr, engine) | 	return http.ListenAndServe(addr, engine) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) RunTLS(addr string, cert string, key string) error { | func (engine *Engine) RunTLS(addr string, cert string, key string) error { | ||||||
|  | 	debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production") | ||||||
| 	debugPrint("Listening and serving HTTPS on %s\n", addr) | 	debugPrint("Listening and serving HTTPS on %s\n", addr) | ||||||
| 	return http.ListenAndServeTLS(addr, cert, key, engine) | 	return http.ListenAndServeTLS(addr, cert, key, engine) | ||||||
| } | } | ||||||
| @ -233,6 +231,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { | |||||||
| 		} else { | 		} else { | ||||||
| 			req.URL.Path = path + "/" | 			req.URL.Path = path + "/" | ||||||
| 		} | 		} | ||||||
|  | 		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) | ||||||
| 		http.Redirect(c.Writer, req, req.URL.String(), code) | 		http.Redirect(c.Writer, req, req.URL.String(), code) | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| @ -245,6 +244,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { | |||||||
| 		) | 		) | ||||||
| 		if found { | 		if found { | ||||||
| 			req.URL.Path = string(fixedPath) | 			req.URL.Path = string(fixedPath) | ||||||
|  | 			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) | ||||||
| 			http.Redirect(c.Writer, req, req.URL.String(), code) | 			http.Redirect(c.Writer, req, req.URL.String(), code) | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -1,69 +0,0 @@ | |||||||
| // 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. |  | ||||||
|  |  | ||||||
| package gin |  | ||||||
|  |  | ||||||
| // 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 "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type inputHolder struct { |  | ||||||
| 	context *Context |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (i inputHolder) FromGET(key string) (va string) { |  | ||||||
| 	va, _ = i.fromGET(key) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (i inputHolder) FromPOST(key string) (va string) { |  | ||||||
| 	va, _ = i.fromPOST(key) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (i inputHolder) Get(key string) string { |  | ||||||
| 	if value, exists := i.fromPOST(key); exists { |  | ||||||
| 		return value |  | ||||||
| 	} |  | ||||||
| 	if value, exists := i.fromGET(key); exists { |  | ||||||
| 		return value |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (i inputHolder) fromGET(key string) (string, bool) { |  | ||||||
| 	req := i.context.Request |  | ||||||
| 	req.ParseForm() |  | ||||||
| 	if values, ok := req.Form[key]; ok && len(values) > 0 { |  | ||||||
| 		return values[0], true |  | ||||||
| 	} |  | ||||||
| 	return "", false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (i inputHolder) fromPOST(key string) (string, bool) { |  | ||||||
| 	req := i.context.Request |  | ||||||
| 	req.ParseForm() |  | ||||||
| 	if values, ok := req.PostForm[key]; ok && len(values) > 0 { |  | ||||||
| 		return values[0], true |  | ||||||
| 	} |  | ||||||
| 	return "", false |  | ||||||
| } |  | ||||||
							
								
								
									
										144
									
								
								middleware_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								middleware_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | |||||||
|  | // 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. | ||||||
|  |  | ||||||
|  | package gin | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  |  | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestMiddlewareGeneralCase(t *testing.T) { | ||||||
|  | 	signature := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "A" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "B" | ||||||
|  | 	}) | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "C" | ||||||
|  | 	}) | ||||||
|  | 	router.GET("/", func(c *Context) { | ||||||
|  | 		signature += "D" | ||||||
|  | 	}) | ||||||
|  | 	router.NoRoute(func(c *Context) { | ||||||
|  | 		signature += "X" | ||||||
|  | 	}) | ||||||
|  | 	router.NoMethod(func(c *Context) { | ||||||
|  | 		signature += "X" | ||||||
|  | 	}) | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 200) | ||||||
|  | 	assert.Equal(t, signature, "ACDB") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers | ||||||
|  | func TestMiddlewareNextOrder(t *testing.T) { | ||||||
|  | 	signature := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "A" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "B" | ||||||
|  | 	}) | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "C" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "D" | ||||||
|  | 	}) | ||||||
|  | 	router.NoRoute(func(c *Context) { | ||||||
|  | 		signature += "E" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "F" | ||||||
|  | 	}, func(c *Context) { | ||||||
|  | 		signature += "G" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "H" | ||||||
|  | 	}) | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 404) | ||||||
|  | 	assert.Equal(t, signature, "ACEGHFDB") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order | ||||||
|  | func TestMiddlewareAbortHandlersChain(t *testing.T) { | ||||||
|  | 	signature := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "A" | ||||||
|  | 	}) | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "C" | ||||||
|  | 		c.AbortWithStatus(409) | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "D" | ||||||
|  | 	}) | ||||||
|  | 	router.GET("/", func(c *Context) { | ||||||
|  | 		signature += "D" | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "E" | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 409) | ||||||
|  | 	assert.Equal(t, signature, "ACD") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { | ||||||
|  | 	signature := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.Use(func(c *Context) { | ||||||
|  | 		signature += "A" | ||||||
|  | 		c.AbortWithStatus(410) | ||||||
|  | 		c.Next() | ||||||
|  | 		signature += "B" | ||||||
|  |  | ||||||
|  | 	}) | ||||||
|  | 	router.GET("/", func(c *Context) { | ||||||
|  | 		signature += "C" | ||||||
|  | 		c.Next() | ||||||
|  | 	}) | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 410) | ||||||
|  | 	assert.Equal(t, signature, "AB") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as | ||||||
|  | // as well as Abort | ||||||
|  | func TestMiddlewareFailHandlersChain(t *testing.T) { | ||||||
|  | 	// SETUP | ||||||
|  | 	signature := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.Use(func(context *Context) { | ||||||
|  | 		signature += "A" | ||||||
|  | 		context.Fail(500, errors.New("foo")) | ||||||
|  | 	}) | ||||||
|  | 	router.Use(func(context *Context) { | ||||||
|  | 		signature += "B" | ||||||
|  | 		context.Next() | ||||||
|  | 		signature += "C" | ||||||
|  | 	}) | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 500) | ||||||
|  | 	assert.Equal(t, signature, "A") | ||||||
|  | } | ||||||
| @ -5,6 +5,7 @@ | |||||||
| package render | package render | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
| @ -19,24 +20,19 @@ func (r *HTMLDebugRender) Render(w http.ResponseWriter, code int, data ...interf | |||||||
| 	file := data[0].(string) | 	file := data[0].(string) | ||||||
| 	obj := data[1] | 	obj := data[1] | ||||||
|  |  | ||||||
| 	if t, err := r.newTemplate(); err == nil { | 	if t, err := r.loadTemplate(); err == nil { | ||||||
| 		return t.ExecuteTemplate(w, file, obj) | 		return t.ExecuteTemplate(w, file, obj) | ||||||
| 	} else { | 	} else { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *HTMLDebugRender) newTemplate() (*template.Template, error) { | func (r *HTMLDebugRender) loadTemplate() (*template.Template, error) { | ||||||
| 	t := template.New("") |  | ||||||
| 	if len(r.Files) > 0 { | 	if len(r.Files) > 0 { | ||||||
| 		if _, err := t.ParseFiles(r.Files...); err != nil { | 		return template.ParseFiles(r.Files...) | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	if len(r.Glob) > 0 { | 	if len(r.Glob) > 0 { | ||||||
| 		if _, err := t.ParseGlob(r.Glob); err != nil { | 		return template.ParseGlob(r.Glob) | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return t, nil | 	return nil, errors.New("the HTML debug render was created without files or glob pattern") | ||||||
| } | } | ||||||
|  | |||||||
| @ -75,5 +75,4 @@ func TestRenderJoinStrings(t *testing.T) { | |||||||
| 	assert.Equal(t, joinStrings("a", "BB", "c"), "aBBc") | 	assert.Equal(t, joinStrings("a", "BB", "c"), "aBBc") | ||||||
| 	assert.Equal(t, joinStrings("a", "", "c"), "ac") | 	assert.Equal(t, joinStrings("a", "", "c"), "ac") | ||||||
| 	assert.Equal(t, joinStrings("text/html", "; charset=utf-8"), "text/html; charset=utf-8") | 	assert.Equal(t, joinStrings("text/html", "; charset=utf-8"), "text/html; charset=utf-8") | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										174
									
								
								routes_test.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								routes_test.go
									
									
									
									
									
								
							| @ -5,7 +5,6 @@ | |||||||
| package gin | package gin | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
| @ -107,8 +106,26 @@ func TestRouteNotOK2(t *testing.T) { | |||||||
| 	testRouteNotOK2("HEAD", t) | 	testRouteNotOK2("HEAD", t) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // TestContextParamsGet tests that a parameter can be parsed from the URL. | ||||||
|  | func TestRouteParamsByName(t *testing.T) { | ||||||
|  | 	name := "" | ||||||
|  | 	lastName := "" | ||||||
|  | 	router := New() | ||||||
|  | 	router.GET("/test/:name/:last_name", func(c *Context) { | ||||||
|  | 		name = c.Params.ByName("name") | ||||||
|  | 		lastName = c.Params.ByName("last_name") | ||||||
|  | 	}) | ||||||
|  | 	// RUN | ||||||
|  | 	w := performRequest(router, "GET", "/test/john/smith") | ||||||
|  |  | ||||||
|  | 	// TEST | ||||||
|  | 	assert.Equal(t, w.Code, 200) | ||||||
|  | 	assert.Equal(t, name, "john") | ||||||
|  | 	assert.Equal(t, lastName, "smith") | ||||||
|  | } | ||||||
|  |  | ||||||
| // TestHandleStaticFile - ensure the static file handles properly | // TestHandleStaticFile - ensure the static file handles properly | ||||||
| func TestHandleStaticFile(t *testing.T) { | func TestRouteStaticFile(t *testing.T) { | ||||||
| 	// SETUP file | 	// SETUP file | ||||||
| 	testRoot, _ := os.Getwd() | 	testRoot, _ := os.Getwd() | ||||||
| 	f, err := ioutil.TempFile(testRoot, "") | 	f, err := ioutil.TempFile(testRoot, "") | ||||||
| @ -134,7 +151,7 @@ func TestHandleStaticFile(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // TestHandleStaticDir - ensure the root/sub dir handles properly | // TestHandleStaticDir - ensure the root/sub dir handles properly | ||||||
| func TestHandleStaticDir(t *testing.T) { | func TestRouteStaticDir(t *testing.T) { | ||||||
| 	// SETUP | 	// SETUP | ||||||
| 	r := New() | 	r := New() | ||||||
| 	r.Static("/", "./") | 	r.Static("/", "./") | ||||||
| @ -151,7 +168,7 @@ func TestHandleStaticDir(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // TestHandleHeadToDir - ensure the root/sub dir handles properly | // TestHandleHeadToDir - ensure the root/sub dir handles properly | ||||||
| func TestHandleHeadToDir(t *testing.T) { | func TestRouteHeadToDir(t *testing.T) { | ||||||
| 	// SETUP | 	// SETUP | ||||||
| 	router := New() | 	router := New() | ||||||
| 	router.Static("/", "./") | 	router.Static("/", "./") | ||||||
| @ -166,152 +183,3 @@ func TestHandleHeadToDir(t *testing.T) { | |||||||
| 	assert.Contains(t, bodyAsString, "gin.go") | 	assert.Contains(t, bodyAsString, "gin.go") | ||||||
| 	assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") | 	assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestContextGeneralCase(t *testing.T) { |  | ||||||
| 	signature := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "A" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "B" |  | ||||||
| 	}) |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "C" |  | ||||||
| 	}) |  | ||||||
| 	router.GET("/", func(c *Context) { |  | ||||||
| 		signature += "D" |  | ||||||
| 	}) |  | ||||||
| 	router.NoRoute(func(c *Context) { |  | ||||||
| 		signature += "X" |  | ||||||
| 	}) |  | ||||||
| 	router.NoMethod(func(c *Context) { |  | ||||||
| 		signature += "X" |  | ||||||
| 	}) |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 200) |  | ||||||
| 	assert.Equal(t, signature, "ACDB") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers |  | ||||||
| func TestContextNextOrder(t *testing.T) { |  | ||||||
| 	signature := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "A" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "B" |  | ||||||
| 	}) |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "C" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "D" |  | ||||||
| 	}) |  | ||||||
| 	router.NoRoute(func(c *Context) { |  | ||||||
| 		signature += "E" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "F" |  | ||||||
| 	}, func(c *Context) { |  | ||||||
| 		signature += "G" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "H" |  | ||||||
| 	}) |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 404) |  | ||||||
| 	assert.Equal(t, signature, "ACEGHFDB") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order |  | ||||||
| func TestAbortHandlersChain(t *testing.T) { |  | ||||||
| 	signature := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "A" |  | ||||||
| 	}) |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "C" |  | ||||||
| 		c.AbortWithStatus(409) |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "D" |  | ||||||
| 	}) |  | ||||||
| 	router.GET("/", func(c *Context) { |  | ||||||
| 		signature += "D" |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "E" |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 409) |  | ||||||
| 	assert.Equal(t, signature, "ACD") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestAbortHandlersChainAndNext(t *testing.T) { |  | ||||||
| 	signature := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.Use(func(c *Context) { |  | ||||||
| 		signature += "A" |  | ||||||
| 		c.AbortWithStatus(410) |  | ||||||
| 		c.Next() |  | ||||||
| 		signature += "B" |  | ||||||
|  |  | ||||||
| 	}) |  | ||||||
| 	router.GET("/", func(c *Context) { |  | ||||||
| 		signature += "C" |  | ||||||
| 		c.Next() |  | ||||||
| 	}) |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 410) |  | ||||||
| 	assert.Equal(t, signature, "AB") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestContextParamsGet tests that a parameter can be parsed from the URL. |  | ||||||
| func TestContextParamsByName(t *testing.T) { |  | ||||||
| 	name := "" |  | ||||||
| 	lastName := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.GET("/test/:name/:last_name", func(c *Context) { |  | ||||||
| 		name = c.Params.ByName("name") |  | ||||||
| 		lastName = c.Params.ByName("last_name") |  | ||||||
| 	}) |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/test/john/smith") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 200) |  | ||||||
| 	assert.Equal(t, name, "john") |  | ||||||
| 	assert.Equal(t, lastName, "smith") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as |  | ||||||
| // as well as Abort |  | ||||||
| func TestFailHandlersChain(t *testing.T) { |  | ||||||
| 	// SETUP |  | ||||||
| 	signature := "" |  | ||||||
| 	router := New() |  | ||||||
| 	router.Use(func(context *Context) { |  | ||||||
| 		signature += "A" |  | ||||||
| 		context.Fail(500, errors.New("foo")) |  | ||||||
| 	}) |  | ||||||
| 	router.Use(func(context *Context) { |  | ||||||
| 		signature += "B" |  | ||||||
| 		context.Next() |  | ||||||
| 		signature += "C" |  | ||||||
| 	}) |  | ||||||
| 	// RUN |  | ||||||
| 	w := performRequest(router, "GET", "/") |  | ||||||
|  |  | ||||||
| 	// TEST |  | ||||||
| 	assert.Equal(t, w.Code, 500) |  | ||||||
| 	assert.Equal(t, signature, "A") |  | ||||||
| } |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user