From 05da3fa2dc473d7cce53fcf4490a586404859d87 Mon Sep 17 00:00:00 2001 From: Vince Yuan Date: Tue, 2 Jun 2015 23:06:08 +0800 Subject: [PATCH 01/17] Updated README.md to show how to use templates with same name in different directories. --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index 01955567..de79ec16 100644 --- a/README.md +++ b/README.md @@ -401,6 +401,7 @@ func main() { router.Run(":8080") } ``` +templates/index.tmpl ```html

{{ .title }} @@ -408,6 +409,46 @@ func main() { ``` +Using templates with same name in different directories + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/post/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "post/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/user/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "user/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") +} +``` +templates/post/index.tmpl +```html +{{ define "post/index.tmpl" }} +

+ {{ .title }} +

+

Using post/index.tmpl

+ +{{ end }} +``` +templates/user/index.tmpl +```html +{{ define "user/index.tmpl" }} +

+ {{ .title }} +

+

Using user/index.tmpl

+ +{{ end }} +``` + You can also use your own html template render ```go From 7229c488e80e33fa1bedf1a02a4584a4cd166d38 Mon Sep 17 00:00:00 2001 From: Vince Yuan Date: Tue, 2 Jun 2015 23:12:00 +0800 Subject: [PATCH 02/17] Use plural for template folder name in the example. --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index de79ec16..8b655b99 100644 --- a/README.md +++ b/README.md @@ -415,36 +415,36 @@ Using templates with same name in different directories func main() { router := gin.Default() router.LoadHTMLGlob("templates/**/*") - router.GET("/post/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "post/index.tmpl", gin.H{ + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ "title": "Posts", }) }) - router.GET("/user/index", func(c *gin.Context) { - c.HTML(http.StatusOK, "user/index.tmpl", gin.H{ + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ "title": "Users", }) }) router.Run(":8080") } ``` -templates/post/index.tmpl +templates/posts/index.tmpl ```html -{{ define "post/index.tmpl" }} +{{ define "posts/index.tmpl" }}

{{ .title }}

-

Using post/index.tmpl

+

Using posts/index.tmpl

{{ end }} ``` -templates/user/index.tmpl +templates/users/index.tmpl ```html -{{ define "user/index.tmpl" }} +{{ define "users/index.tmpl" }}

{{ .title }}

-

Using user/index.tmpl

+

Using users/index.tmpl

{{ end }} ``` From f831ac80ac8f283c0a0431e0a0f6b772ce113dca Mon Sep 17 00:00:00 2001 From: danielalves Date: Tue, 2 Jun 2015 17:16:51 -0300 Subject: [PATCH 03/17] Exporting CreateTestContext --- context_test.go | 71 +++++++++++++++++++++---------------------------- test_helpers.go | 14 ++++++++++ 2 files changed, 45 insertions(+), 40 deletions(-) create mode 100644 test_helpers.go diff --git a/context_test.go b/context_test.go index cc1c4527..629e145c 100644 --- a/context_test.go +++ b/context_test.go @@ -24,15 +24,6 @@ import ( // BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) { // test that information is not leaked when reusing Contexts (using the Pool) -func createTestContext() (c *Context, w *httptest.ResponseRecorder, r *Engine) { - w = httptest.NewRecorder() - r = New() - c = r.allocateContext() - c.reset() - c.writermem.reset(w) - return -} - func createMultipartRequest() *http.Request { boundary := "--testboundary" body := new(bytes.Buffer) @@ -80,7 +71,7 @@ func TestContextReset(t *testing.T) { // TestContextSetGet tests that a parameter is set correctly on the // current context and can be retrieved using Get. func TestContextSetGet(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Set("foo", "bar") value, err := c.Get("foo") @@ -96,7 +87,7 @@ func TestContextSetGet(t *testing.T) { } func TestContextSetGetValues(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Set("string", "this is a string") c.Set("int32", int32(-42)) c.Set("int64", int64(42424242424242)) @@ -117,7 +108,7 @@ func TestContextSetGetValues(t *testing.T) { } func TestContextCopy(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.index = 2 c.Request, _ = http.NewRequest("POST", "/hola", nil) c.handlers = HandlersChain{func(c *Context) {}} @@ -136,7 +127,7 @@ func TestContextCopy(t *testing.T) { } func TestContextQuery(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10", nil) assert.Equal(t, c.DefaultQuery("foo", "none"), "bar") @@ -153,7 +144,7 @@ func TestContextQuery(t *testing.T) { } func TestContextQueryAndPostForm(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() body := bytes.NewBufferString("foo=bar&page=11&both=POST") c.Request, _ = http.NewRequest("POST", "/?both=GET&id=main", body) c.Request.Header.Add("Content-Type", MIMEPOSTForm) @@ -191,7 +182,7 @@ func TestContextQueryAndPostForm(t *testing.T) { } func TestContextPostFormMultipart(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request = createMultipartRequest() var obj struct { @@ -211,7 +202,7 @@ func TestContextPostFormMultipart(t *testing.T) { // Tests that the response is serialized as JSON // and Content-Type is set to application/json func TestContextRenderJSON(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.JSON(201, H{"foo": "bar"}) assert.Equal(t, w.Code, 201) @@ -222,7 +213,7 @@ func TestContextRenderJSON(t *testing.T) { // Tests that the response is serialized as JSON // and Content-Type is set to application/json func TestContextRenderIndentedJSON(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) assert.Equal(t, w.Code, 201) @@ -233,7 +224,7 @@ func TestContextRenderIndentedJSON(t *testing.T) { // Tests that the response executes the templates // and responds with Content-Type set to text/html func TestContextRenderHTML(t *testing.T) { - c, w, router := createTestContext() + c, w, router := CreateTestContext() templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) router.SetHTMLTemplate(templ) @@ -247,7 +238,7 @@ func TestContextRenderHTML(t *testing.T) { // TestContextXML tests that the response is serialized as XML // and Content-Type is set to application/xml func TestContextRenderXML(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.XML(201, H{"foo": "bar"}) assert.Equal(t, w.Code, 201) @@ -258,7 +249,7 @@ func TestContextRenderXML(t *testing.T) { // TestContextString tests that the response is returned // with Content-Type set to text/plain func TestContextRenderString(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.String(201, "test %s %d", "string", 2) assert.Equal(t, w.Code, 201) @@ -269,7 +260,7 @@ func TestContextRenderString(t *testing.T) { // TestContextString tests that the response is returned // with Content-Type set to text/html func TestContextRenderHTMLString(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Header("Content-Type", "text/html; charset=utf-8") c.String(201, "%s %d", "string", 3) @@ -281,7 +272,7 @@ func TestContextRenderHTMLString(t *testing.T) { // TestContextData tests that the response can be written from `bytesting` // with specified MIME type func TestContextRenderData(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Data(201, "text/csv", []byte(`foo,bar`)) assert.Equal(t, w.Code, 201) @@ -290,7 +281,7 @@ func TestContextRenderData(t *testing.T) { } func TestContextRenderSSE(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.SSEvent("float", 1.5) c.Render(-1, sse.Event{ Id: "123", @@ -305,7 +296,7 @@ func TestContextRenderSSE(t *testing.T) { } func TestContextRenderFile(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Request, _ = http.NewRequest("GET", "/", nil) c.File("./gin.go") @@ -315,7 +306,7 @@ func TestContextRenderFile(t *testing.T) { } func TestContextHeaders(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Header("Content-Type", "text/plain") c.Header("X-Custom", "value") @@ -332,7 +323,7 @@ func TestContextHeaders(t *testing.T) { // TODO func TestContextRenderRedirectWithRelativePath(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "http://example.com", nil) assert.Panics(t, func() { c.Redirect(299, "/new_path") }) assert.Panics(t, func() { c.Redirect(309, "/new_path") }) @@ -344,7 +335,7 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) { } func TestContextRenderRedirectWithAbsolutePath(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Redirect(302, "http://google.com") c.Writer.WriteHeaderNow() @@ -354,7 +345,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) { } func TestContextNegotiationFormat(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "", nil) assert.Panics(t, func() { c.NegotiateFormat() }) @@ -363,7 +354,7 @@ func TestContextNegotiationFormat(t *testing.T) { } func TestContextNegotiationFormatWithAccept(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") @@ -373,7 +364,7 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) { } func TestContextNegotiationFormatCustum(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") @@ -388,7 +379,7 @@ func TestContextNegotiationFormatCustum(t *testing.T) { // TestContextData tests that the response can be written from `bytesting` // with specified MIME type func TestContextAbortWithStatus(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.index = 4 c.AbortWithStatus(401) c.Writer.WriteHeaderNow() @@ -400,7 +391,7 @@ func TestContextAbortWithStatus(t *testing.T) { } func TestContextError(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() assert.Empty(t, c.Errors) c.Error(errors.New("first error")) @@ -426,7 +417,7 @@ func TestContextError(t *testing.T) { } func TestContextTypedError(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) @@ -440,7 +431,7 @@ func TestContextTypedError(t *testing.T) { } func TestContextAbortWithError(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.AbortWithError(401, errors.New("bad input")).SetMeta("some input") c.Writer.WriteHeaderNow() @@ -450,7 +441,7 @@ func TestContextAbortWithError(t *testing.T) { } func TestContextClientIP(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ") @@ -470,7 +461,7 @@ func TestContextClientIP(t *testing.T) { } func TestContextContentType(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", nil) c.Request.Header.Set("Content-Type", "application/json; charset=utf-8") @@ -478,7 +469,7 @@ func TestContextContentType(t *testing.T) { } func TestContextAutoBindJSON(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) @@ -493,7 +484,7 @@ func TestContextAutoBindJSON(t *testing.T) { } func TestContextBindWithJSON(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type @@ -508,7 +499,7 @@ func TestContextBindWithJSON(t *testing.T) { } func TestContextBadAutoBind(t *testing.T) { - c, w, _ := createTestContext() + c, w, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request.Header.Add("Content-Type", MIMEJSON) var obj struct { @@ -527,7 +518,7 @@ func TestContextBadAutoBind(t *testing.T) { } func TestContextGolangContext(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) assert.NoError(t, c.Err()) assert.Nil(t, c.Done()) diff --git a/test_helpers.go b/test_helpers.go new file mode 100644 index 00000000..7d8020c3 --- /dev/null +++ b/test_helpers.go @@ -0,0 +1,14 @@ +package gin + +import ( + "net/http/httptest" +) + +func CreateTestContext() (c *Context, w *httptest.ResponseRecorder, r *Engine) { + w = httptest.NewRecorder() + r = New() + c = r.allocateContext() + c.reset() + c.writermem.reset(w) + return +} From c1719f7e2063acd931be65aae2c8bad27f2fc137 Mon Sep 17 00:00:00 2001 From: Donn Pebe Date: Thu, 2 Jul 2015 01:48:21 +0700 Subject: [PATCH 04/17] Use only the ip part of request RemoteAddr --- context.go | 6 +++++- context_test.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 12920fd8..8948a9f7 100644 --- a/context.go +++ b/context.go @@ -8,6 +8,7 @@ import ( "errors" "io" "math" + "net" "net/http" "strings" "time" @@ -269,7 +270,10 @@ func (c *Context) ClientIP() string { return clientIP } } - return strings.TrimSpace(c.Request.RemoteAddr) + if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil { + return ip + } + return "" } func (c *Context) ContentType() string { diff --git a/context_test.go b/context_test.go index 9b78992b..9d1fc544 100644 --- a/context_test.go +++ b/context_test.go @@ -478,7 +478,7 @@ func TestContextClientIP(t *testing.T) { c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ") c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30") - c.Request.RemoteAddr = " 40.40.40.40 " + c.Request.RemoteAddr = " 40.40.40.40:42123 " assert.Equal(t, c.ClientIP(), "10.10.10.10") From 9d644d22e0c8102d2d66122e43b91ccc3480109e Mon Sep 17 00:00:00 2001 From: zhing Date: Sun, 12 Jul 2015 17:42:39 +0800 Subject: [PATCH 05/17] add protobuf binding for gin --- binding/binding.go | 10 +++- binding/binding_test.go | 34 +++++++++++ binding/example/test.pb.go | 113 +++++++++++++++++++++++++++++++++++++ binding/example/test.proto | 12 ++++ binding/protobuf.go | 35 ++++++++++++ 5 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 binding/example/test.pb.go create mode 100644 binding/example/test.proto create mode 100644 binding/protobuf.go diff --git a/binding/binding.go b/binding/binding.go index f719fbc1..38d45e9a 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -14,6 +14,7 @@ const ( MIMEPlain = "text/plain" MIMEPOSTForm = "application/x-www-form-urlencoded" MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/octet-stream" ) type Binding interface { @@ -33,9 +34,10 @@ type StructValidator interface { var Validator StructValidator = &defaultValidator{} var ( - JSON = jsonBinding{} - XML = xmlBinding{} - Form = formBinding{} + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} + ProtoBuf = protobufBinding{} ) func Default(method, contentType string) Binding { @@ -47,6 +49,8 @@ func Default(method, contentType string) Binding { return JSON case MIMEXML, MIMEXML2: return XML + case MIMEPROTOBUF: + return ProtoBuf default: //case MIMEPOSTForm, MIMEMultipartPOSTForm: return Form } diff --git a/binding/binding_test.go b/binding/binding_test.go index db1678e4..517be437 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -6,6 +6,8 @@ package binding import ( "bytes" + "github.com/gin-gonic/gin/binding/example" + "github.com/golang/protobuf/proto" "net/http" "testing" @@ -36,6 +38,9 @@ func TestBindingDefault(t *testing.T) { assert.Equal(t, Default("POST", MIMEMultipartPOSTForm), Form) assert.Equal(t, Default("PUT", MIMEMultipartPOSTForm), Form) + + assert.Equal(t, Default("POST", MIMEPROTOBUF), ProtoBuf) + assert.Equal(t, Default("PUT", MIMEPROTOBUF), ProtoBuf) } func TestBindingJSON(t *testing.T) { @@ -64,6 +69,18 @@ func TestBindingXML(t *testing.T) { "bar", "foo") } +func TestBindingProtoBuf(t *testing.T) { + test := &example.Test{ + Label: proto.String("yes"), + } + data, _ := proto.Marshal(test) + + testProtoBodyBinding(t, + ProtoBuf, "protobuf", + "/", "/", + string(data), string(data[1:])) +} + func TestValidationFails(t *testing.T) { var obj FooStruct req := requestWithBody("POST", "/", `{"bar": "foo"}`) @@ -117,6 +134,23 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody assert.Error(t, err) } +func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { + assert.Equal(t, b.Name(), name) + + obj := example.Test{} + req := requestWithBody("POST", path, body) + req.Header.Add("Content-Type", MIMEPROTOBUF) + err := b.Bind(req, &obj) + assert.NoError(t, err) + assert.Equal(t, *obj.Label, "yes") + + obj = example.Test{} + req = requestWithBody("POST", badPath, badBody) + req.Header.Add("Content-Type", MIMEPROTOBUF) + err = ProtoBuf.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 diff --git a/binding/example/test.pb.go b/binding/example/test.pb.go new file mode 100644 index 00000000..3de8444f --- /dev/null +++ b/binding/example/test.pb.go @@ -0,0 +1,113 @@ +// Code generated by protoc-gen-go. +// source: test.proto +// DO NOT EDIT! + +/* +Package example is a generated protocol buffer package. + +It is generated from these files: + test.proto + +It has these top-level messages: + Test +*/ +package example + +import proto "github.com/golang/protobuf/proto" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type FOO int32 + +const ( + FOO_X FOO = 17 +) + +var FOO_name = map[int32]string{ + 17: "X", +} +var FOO_value = map[string]int32{ + "X": 17, +} + +func (x FOO) Enum() *FOO { + p := new(FOO) + *p = x + return p +} +func (x FOO) String() string { + return proto.EnumName(FOO_name, int32(x)) +} +func (x *FOO) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO") + if err != nil { + return err + } + *x = FOO(value) + return nil +} + +type Test struct { + Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` + Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` + Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` + Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Test) Reset() { *m = Test{} } +func (m *Test) String() string { return proto.CompactTextString(m) } +func (*Test) ProtoMessage() {} + +const Default_Test_Type int32 = 77 + +func (m *Test) GetLabel() string { + if m != nil && m.Label != nil { + return *m.Label + } + return "" +} + +func (m *Test) GetType() int32 { + if m != nil && m.Type != nil { + return *m.Type + } + return Default_Test_Type +} + +func (m *Test) GetReps() []int64 { + if m != nil { + return m.Reps + } + return nil +} + +func (m *Test) GetOptionalgroup() *Test_OptionalGroup { + if m != nil { + return m.Optionalgroup + } + return nil +} + +type Test_OptionalGroup struct { + RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } +func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } +func (*Test_OptionalGroup) ProtoMessage() {} + +func (m *Test_OptionalGroup) GetRequiredField() string { + if m != nil && m.RequiredField != nil { + return *m.RequiredField + } + return "" +} + +func init() { + proto.RegisterEnum("example.FOO", FOO_name, FOO_value) +} diff --git a/binding/example/test.proto b/binding/example/test.proto new file mode 100644 index 00000000..8ee9800a --- /dev/null +++ b/binding/example/test.proto @@ -0,0 +1,12 @@ +package example; + +enum FOO {X=17;}; + +message Test { + required string label = 1; + optional int32 type = 2[default=77]; + repeated int64 reps = 3; + optional group OptionalGroup = 4{ + required string RequiredField = 5; + } +} diff --git a/binding/protobuf.go b/binding/protobuf.go new file mode 100644 index 00000000..d6bef029 --- /dev/null +++ b/binding/protobuf.go @@ -0,0 +1,35 @@ +// 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 ( + "github.com/golang/protobuf/proto" + + "io/ioutil" + "net/http" +) + +type protobufBinding struct{} + +func (_ protobufBinding) Name() string { + return "protobuf" +} + +func (_ protobufBinding) Bind(req *http.Request, obj interface{}) error { + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + + if err = proto.Unmarshal(buf, obj.(proto.Message)); err != nil { + return err + } + + //Here it's same to return validate(obj), but util now we cann't add `binding:""` to the struct + //which automatically generate by gen-proto + return nil + //return validate(obj) +} From e6cd47a0044a54e42f438a11c2396509c30f5207 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 13 Jul 2015 10:13:26 +0800 Subject: [PATCH 06/17] Correct the query parameter name. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5600e65..8d88a770 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ func main() { router.POST("/post", func(c *gin.Context) { id := c.Query("id") - page := c.DefaultQuery("id", "0") + page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") From 04917e83076ae04a4bc12a7b88d9396e78cb9aba Mon Sep 17 00:00:00 2001 From: zhing Date: Sat, 18 Jul 2015 15:18:01 +0800 Subject: [PATCH 07/17] correct the mime type to x-protobuf --- binding/binding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/binding.go b/binding/binding.go index 38d45e9a..e1b5ceeb 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -14,7 +14,7 @@ const ( MIMEPlain = "text/plain" MIMEPOSTForm = "application/x-www-form-urlencoded" MIMEMultipartPOSTForm = "multipart/form-data" - MIMEPROTOBUF = "application/octet-stream" + MIMEPROTOBUF = "application/x-protobuf" ) type Binding interface { From f13c3ae89848df0f5b72240b986f759280decc8b Mon Sep 17 00:00:00 2001 From: Roman Zaynetdinov Date: Wed, 22 Jul 2015 17:02:36 +0300 Subject: [PATCH 08/17] Add option to skip logging specified endpoints --- logger.go | 51 +++++++++++++++++++++++++++++++------------------- logger_test.go | 14 ++++++++++++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/logger.go b/logger.go index e0f9b367..c5d4c3e2 100644 --- a/logger.go +++ b/logger.go @@ -46,7 +46,17 @@ func Logger() HandlerFunc { // Instance a Logger middleware with the specified writter buffer. // Example: os.Stdout, a file opened in write mode, a socket... -func LoggerWithWriter(out io.Writer) HandlerFunc { +func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { + var skip map[string]struct{} + + if length := len(notlogged); length > 0 { + skip = make(map[string]struct{}, length) + + for _, path := range notlogged { + skip[path] = struct{}{} + } + } + return func(c *Context) { // Start timer start := time.Now() @@ -55,26 +65,29 @@ func LoggerWithWriter(out io.Writer) HandlerFunc { // Process request c.Next() - // Stop timer - end := time.Now() - latency := end.Sub(start) + // Log only when path is not being skipped + if _, ok := skip[path]; !ok { + // Stop timer + end := time.Now() + latency := end.Sub(start) - clientIP := c.ClientIP() - method := c.Request.Method - statusCode := c.Writer.Status() - statusColor := colorForStatus(statusCode) - methodColor := colorForMethod(method) - comment := c.Errors.ByType(ErrorTypePrivate).String() + clientIP := c.ClientIP() + method := c.Request.Method + statusCode := c.Writer.Status() + statusColor := colorForStatus(statusCode) + methodColor := colorForMethod(method) + comment := c.Errors.ByType(ErrorTypePrivate).String() - fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s", - end.Format("2006/01/02 - 15:04:05"), - statusColor, statusCode, reset, - latency, - clientIP, - methodColor, reset, method, - path, - comment, - ) + fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s", + end.Format("2006/01/02 - 15:04:05"), + statusColor, statusCode, reset, + latency, + clientIP, + methodColor, reset, method, + path, + comment, + ) + } } } diff --git a/logger_test.go b/logger_test.go index 267f9c5b..dc6ba64b 100644 --- a/logger_test.go +++ b/logger_test.go @@ -124,3 +124,17 @@ func TestErrorLogger(t *testing.T) { assert.Equal(t, w.Code, 500) assert.Equal(t, w.Body.String(), "hola!") } + +func TestSkippingPaths(t *testing.T) { + buffer := new(bytes.Buffer) + router := New() + router.Use(LoggerWithWriter(buffer, "/skipped")) + router.GET("/logged", func(c *Context) {}) + router.GET("/skipped", func(c *Context) {}) + + performRequest(router, "GET", "/logged") + assert.Contains(t, buffer.String(), "200") + + performRequest(router, "GET", "/skipped") + assert.Contains(t, buffer.String(), "") +} From 39a82ce0b88fa37b37e0624f755907ad16e65ede Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Sun, 16 Aug 2015 18:44:18 +0200 Subject: [PATCH 09/17] ginS -- api experiment --- ginS/README.md | 17 ++++++ ginS/gins.go | 140 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 ginS/README.md create mode 100644 ginS/gins.go diff --git a/ginS/README.md b/ginS/README.md new file mode 100644 index 00000000..c186b1f8 --- /dev/null +++ b/ginS/README.md @@ -0,0 +1,17 @@ +#Gin Default Server + +This is API experiment for Gin. + +```go +package main + +import ( + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/ginS" +) + +func main() { + ginS.GET("/", func(c *gin.Context) { c.String("Hello World") }) + ginS.Run() +} +``` diff --git a/ginS/gins.go b/ginS/gins.go new file mode 100644 index 00000000..71744702 --- /dev/null +++ b/ginS/gins.go @@ -0,0 +1,140 @@ +// 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 ginS + +import ( + "html/template" + "net/http" + "sync" + + . "github.com/gin-gonic/gin" +) + +var once sync.Once +var internalEngine *Engine + +func engine() *Engine { + once.Do(func() { + internalEngine = Default() + }) + return internalEngine +} + +func LoadHTMLGlob(pattern string) { + engine().LoadHTMLGlob(pattern) +} + +func LoadHTMLFiles(files ...string) { + engine().LoadHTMLFiles(files...) +} + +func SetHTMLTemplate(templ *template.Template) { + engine().SetHTMLTemplate(templ) +} + +// Adds handlers for NoRoute. It return a 404 code by default. +func NoRoute(handlers ...HandlerFunc) { + engine().NoRoute(handlers...) +} + +// Sets the handlers called when... TODO +func NoMethod(handlers ...HandlerFunc) { + engine().NoMethod(handlers...) +} + +// Creates a new router group. You should add all the routes that have common middlwares or the same path prefix. +// For example, all the routes that use a common middlware for authorization could be grouped. +func Group(relativePath string, handlers ...HandlerFunc) *RouterGroup { + return engine().Group(relativePath, handlers...) +} + +func Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().Handle(httpMethod, relativePath, handlers...) +} + +// POST is a shortcut for router.Handle("POST", path, handle) +func POST(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().POST(relativePath, handlers...) +} + +// GET is a shortcut for router.Handle("GET", path, handle) +func GET(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().GET(relativePath, handlers...) +} + +// DELETE is a shortcut for router.Handle("DELETE", path, handle) +func DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().DELETE(relativePath, handlers...) +} + +// PATCH is a shortcut for router.Handle("PATCH", path, handle) +func PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().PATCH(relativePath, handlers...) +} + +// PUT is a shortcut for router.Handle("PUT", path, handle) +func PUT(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().PUT(relativePath, handlers...) +} + +// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle) +func OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().OPTIONS(relativePath, handlers...) +} + +// HEAD is a shortcut for router.Handle("HEAD", path, handle) +func HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().HEAD(relativePath, handlers...) +} + +func Any(relativePath string, handlers ...HandlerFunc) IRoutes { + return engine().Any(relativePath, handlers...) +} + +func StaticFile(relativePath, filepath string) IRoutes { + return engine().StaticFile(relativePath, filepath) +} + +// 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 Static(relativePath, root string) IRoutes { + return engine().Static(relativePath, root) +} + +func StaticFS(relativePath string, fs http.FileSystem) IRoutes { + return engine().StaticFS(relativePath, fs) +} + +// Attachs a global middleware to the router. ie. the middlewares attached though Use() will be +// included in the handlers chain for every single request. Even 404, 405, static files... +// For example, this is the right place for a logger or error management middleware. +func Use(middlewares ...HandlerFunc) IRoutes { + return engine().Use(middlewares...) +} + +// The router is attached to a http.Server and starts listening and serving HTTP requests. +// It is a shortcut for http.ListenAndServe(addr, router) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func Run(addr ...string) (err error) { + return engine().Run(addr...) +} + +// The router is attached to a http.Server and starts listening and serving HTTPS requests. +// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func RunTLS(addr string, certFile string, keyFile string) (err error) { + return engine().RunTLS(addr, certFile, keyFile) +} + +// The router is attached to a http.Server and starts listening and serving HTTP requests +// through the specified unix socket (ie. a file) +// Note: this method will block the calling goroutine undefinitelly unless an error happens. +func RunUnix(file string) (err error) { + return engine().RunUnix(file) +} From dba1781814253c1b16519a65a567f17f94fb8feb Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Mon, 17 Aug 2015 12:27:38 +0200 Subject: [PATCH 10/17] Correct an error in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e83952d6..a5d4591b 100644 --- a/README.md +++ b/README.md @@ -191,18 +191,18 @@ func main() { router.POST("/post", func(c *gin.Context) { id := c.Query("id") - page := c.DefaultQuery("id", "0") + page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") - fmt.Println("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + fmt.Print("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080") } ``` ``` -id: 1234; page: 0; name: manu; message: this_is_great +id: 1234; page: 1; name: manu; message: this_is_great ``` From 02df324cc600500b1bc2590289b42f644c49ab68 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Mon, 17 Aug 2015 12:49:28 +0200 Subject: [PATCH 11/17] Fix #406, #407 and gofmt all examples --- README.md | 137 +++++++++++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index a5d4591b..e84fa340 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Gin is a web framework written in Golang. It features a martini-like API with mu ![Gin console logger](https://gin-gonic.github.io/gin/other/console.png) -``` +```sh $ cat test.go ``` ```go @@ -84,7 +84,7 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 1. Download and install it: ```sh -go get github.com/gin-gonic/gin +$ go get github.com/gin-gonic/gin ``` 2. Import it in your code: @@ -143,17 +143,17 @@ func main() { #### Querystring parameters ```go func main() { - router := gin.Default() + router := gin.Default() - // Query string parameters are parsed using the existing underlying request object. - // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe - router.GET("/welcome", func(c *gin.Context) { - firstname := c.DefaultQuery("firstname", "Guest") - lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + // Query string parameters are parsed using the existing underlying request object. + // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") - c.String(http.StatusOK, "Hello %s %s", firstname, lastname) - }) - router.Run(":8080") + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") } ``` @@ -161,18 +161,19 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - router.POST("/form_post", func(c *gin.Context) { - message := c.PostForm("message") - nick := c.DefaultPostForm("nick", "anonymous") + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") - c.JSON(200, gin.H{ - "status": "posted", - "message": message, - }) - }) - router.Run(":8080") + c.JSON(200, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") } ``` @@ -190,12 +191,12 @@ func main() { router := gin.Default() router.POST("/post", func(c *gin.Context) { - id := c.Query("id") - page := c.DefaultQuery("page", "0") - name := c.PostForm("name") - message := c.PostForm("message") + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") - fmt.Print("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080") } @@ -301,30 +302,30 @@ type Login struct { func main() { router := gin.Default() - // Example for binding JSON ({"user": "manu", "password": "123"}) + // Example for binding JSON ({"user": "manu", "password": "123"}) router.POST("/loginJSON", func(c *gin.Context) { var json Login - if c.BindJSON(&json) == nil { - if json.User == "manu" && json.Password == "123" { - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - } else { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - } - } + if c.BindJSON(&json) == nil { + if json.User == "manu" && json.Password == "123" { + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + } + } }) - // Example for binding a HTML form (user=manu&password=123) - router.POST("/loginForm", func(c *gin.Context) { - var form Login - // This will infer what binder to use depending on the content-type header. - if c.Bind(&form) == nil { - if form.User == "manu" && form.Password == "123" { - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - } else { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - } - } - }) + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if c.Bind(&form) == nil { + if form.User == "manu" && form.Password == "123" { + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + } + } + }) // Listen and server on 0.0.0.0:8080 router.Run(":8080") @@ -353,21 +354,21 @@ func main() { // c.BindWith(&form, binding.Form) // or you can simply use autobinding with Bind method: var form LoginForm - // in this case proper binding will be automatically selected + // in this case proper binding will be automatically selected if c.Bind(&form) == nil { - if form.User == "user" && form.Password == "password" { - c.JSON(200, gin.H{"status": "you are logged in"}) - } else { - c.JSON(401, gin.H{"status": "unauthorized"}) - } - } + if form.User == "user" && form.Password == "password" { + c.JSON(200, gin.H{"status": "you are logged in"}) + } else { + c.JSON(401, gin.H{"status": "unauthorized"}) + } + } }) router.Run(":8080") } ``` Test it with: -```bash +```sh $ curl -v --form user=user --form password=password http://localhost:8080/login ``` @@ -411,13 +412,13 @@ func main() { ```go func main() { - router := gin.Default() - router.Static("/assets", "./assets") - router.StaticFS("/more_static", http.Dir("my_file_system")) - router.StaticFile("/favicon.ico", "./resources/favicon.ico") + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") - // Listen and server on 0.0.0.0:8080 - router.Run(":8080") + // Listen and server on 0.0.0.0:8080 + router.Run(":8080") } ``` @@ -439,9 +440,10 @@ func main() { } ``` ```html -

- {{ .title }} -

+ +

+ {{ .title }} +

``` @@ -559,17 +561,16 @@ func main() { r.GET("/long_async", func(c *gin.Context) { // create copy to be used inside the goroutine - c_cp := c.Copy() + cCp := c.Copy() go func() { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // note than you are using the copied context "c_cp", IMPORTANT - log.Println("Done! in path " + c_cp.Request.URL.Path) + log.Println("Done! in path " + cCp.Request.URL.Path) }() }) - r.GET("/long_sync", func(c *gin.Context) { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) @@ -578,8 +579,8 @@ func main() { log.Println("Done! in path " + c.Request.URL.Path) }) - // Listen and server on 0.0.0.0:8080 - r.Run(":8080") + // Listen and server on 0.0.0.0:8080 + r.Run(":8080") } ``` From f5b1fb44bb819bd8059a84998c3a902bd01d92b3 Mon Sep 17 00:00:00 2001 From: Damon Zhao Date: Thu, 27 Aug 2015 16:04:50 +0800 Subject: [PATCH 12/17] Add SetCookie and GetCookie method for Context --- context.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ context_test.go | 17 ++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/context.go b/context.go index 20a20e33..c4fb96f8 100644 --- a/context.go +++ b/context.go @@ -9,6 +9,7 @@ import ( "io" "math" "net/http" + "net/url" "strings" "time" @@ -321,6 +322,64 @@ func (c *Context) Header(key, value string) { } } +func (c *Context) SetCookie(name string, value string, options ...interface{}) { + cookie := http.Cookie{} + cookie.Name = name + cookie.Value = url.QueryEscape(value) + + if len(options) > 0 { + switch v := options[0].(type) { + case int: + cookie.MaxAge = v + case int64: + cookie.MaxAge = int(v) + case int32: + cookie.MaxAge = int(v) + } + } + + cookie.Path = "/" + if len(options) > 1 { + if v, ok := options[1].(string); ok && len(v) > 0 { + cookie.Path = v + } + } + + if len(options) > 2 { + if v, ok := options[2].(string); ok && len(v) > 0 { + cookie.Domain = v + } + } + + if len(options) > 3 { + switch v := options[3].(type) { + case bool: + cookie.Secure = v + default: + if options[3] != nil { + cookie.Secure = true + } + } + } + + if len(options) > 4 { + if v, ok := options[4].(bool); ok && v { + cookie.HttpOnly = true + } + } + + c.Writer.Header().Add("Set-Cookie", cookie.String()) +} + +func (c *Context) GetCookie(name string) string { + cookie, err := c.Request.Cookie(name) + if err != nil { + return "" + } + val, _ := url.QueryUnescape(cookie.Value) + return val +} + func (c *Context) Render(code int, r render.Render) { c.writermem.WriteHeader(code) if err := r.Render(c.Writer); err != nil { diff --git a/context_test.go b/context_test.go index d95125ae..5697a04d 100644 --- a/context_test.go +++ b/context_test.go @@ -246,6 +246,23 @@ func TestContextPostFormMultipart(t *testing.T) { assert.Equal(t, c.PostForm("bar"), "foo") } +func TestContextSetCookie(t *testing.T) { + c, w, _ := createTestContext() + c.SetCookie("user", "gin", 1, "/", "localhost", true, true) + c.SetCookie("user", "gin", int32(1), "/", "localhost", 1) + c.SetCookie("user", "gin", int64(1)) + + c.Request, _ = http.NewRequest("GET", "/set", nil) + assert.Equal(t, c.GetCookie("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") +} + +func TestContextGetCookie(t *testing.T) { + c, w, _ := createTestContext() + c.Request, _ = http.NewRequest("GET", "/get", nil) + c.Request.Header.Set("Cookie", "user=gin") + assert.Equal(t, w.Body.String(), "gin") +} + // Tests that the response is serialized as JSON // and Content-Type is set to application/json func TestContextRenderJSON(t *testing.T) { From 7bf97883262733f7534fce486727ed6cea1e86bb Mon Sep 17 00:00:00 2001 From: Damon Zhao Date: Thu, 27 Aug 2015 16:16:16 +0800 Subject: [PATCH 13/17] fix ci --- context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 5697a04d..cb704bb7 100644 --- a/context_test.go +++ b/context_test.go @@ -260,7 +260,7 @@ func TestContextGetCookie(t *testing.T) { c, w, _ := createTestContext() c.Request, _ = http.NewRequest("GET", "/get", nil) c.Request.Header.Set("Cookie", "user=gin") - assert.Equal(t, w.Body.String(), "gin") + assert.Equal(t, c.GetCookie("Cookie"), "gin") } // Tests that the response is serialized as JSON From e1d27558b3b1aa95a864036b1960fe2c267fb3ea Mon Sep 17 00:00:00 2001 From: Damon Zhao Date: Tue, 8 Sep 2015 15:37:20 +0800 Subject: [PATCH 14/17] add parameters for set cookie --- context.go | 52 +++++++++++++++------------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/context.go b/context.go index c4fb96f8..37b11003 100644 --- a/context.go +++ b/context.go @@ -322,51 +322,29 @@ func (c *Context) Header(key, value string) { } } -func (c *Context) SetCookie(name string, value string, options ...interface{}) { +func (c *Context) SetCookie( + name string, + value string, + maxAge int, + path string, + domain string, + secure bool, + httpOnly bool, +) { cookie := http.Cookie{} cookie.Name = name cookie.Value = url.QueryEscape(value) - if len(options) > 0 { - switch v := options[0].(type) { - case int: - cookie.MaxAge = v - case int64: - cookie.MaxAge = int(v) - case int32: - cookie.MaxAge = int(v) - } - } + cookie.MaxAge = maxAge cookie.Path = "/" - if len(options) > 1 { - if v, ok := options[1].(string); ok && len(v) > 0 { - cookie.Path = v - } + if path != "" { + cookie.Path = path } - if len(options) > 2 { - if v, ok := options[2].(string); ok && len(v) > 0 { - cookie.Domain = v - } - } - - if len(options) > 3 { - switch v := options[3].(type) { - case bool: - cookie.Secure = v - default: - if options[3] != nil { - cookie.Secure = true - } - } - } - - if len(options) > 4 { - if v, ok := options[4].(bool); ok && v { - cookie.HttpOnly = true - } - } + cookie.Domain = domain + cookie.Secure = secure + cookie.HttpOnly = httpOnly c.Writer.Header().Add("Set-Cookie", cookie.String()) } From 8e37eb84984714af6cea105d1885798b70025444 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Fri, 2 Oct 2015 12:37:51 +0200 Subject: [PATCH 15/17] Fix tests for GetCookie() and SetCookie() --- context.go | 6 +++--- context_test.go | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/context.go b/context.go index 4bf79860..a636f7aa 100644 --- a/context.go +++ b/context.go @@ -349,13 +349,13 @@ func (c *Context) SetCookie( c.Writer.Header().Add("Set-Cookie", cookie.String()) } -func (c *Context) GetCookie(name string) string { +func (c *Context) GetCookie(name string) (string, error) { cookie, err := c.Request.Cookie(name) if err != nil { - return "" + return "", err } val, _ := url.QueryUnescape(cookie.Value) - return val + return val, nil } func (c *Context) Render(code int, r render.Render) { diff --git a/context_test.go b/context_test.go index 18e54600..c6bb591f 100644 --- a/context_test.go +++ b/context_test.go @@ -248,20 +248,18 @@ func TestContextPostFormMultipart(t *testing.T) { } func TestContextSetCookie(t *testing.T) { - c, w, _ := createTestContext() + c, _, _ := createTestContext() c.SetCookie("user", "gin", 1, "/", "localhost", true, true) - c.SetCookie("user", "gin", int32(1), "/", "localhost", 1) - c.SetCookie("user", "gin", int64(1)) - c.Request, _ = http.NewRequest("GET", "/set", nil) - assert.Equal(t, c.GetCookie("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") + assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") } func TestContextGetCookie(t *testing.T) { - c, w, _ := createTestContext() + c, _, _ := createTestContext() c.Request, _ = http.NewRequest("GET", "/get", nil) c.Request.Header.Set("Cookie", "user=gin") - assert.Equal(t, c.GetCookie("Cookie"), "gin") + cookie, _ := c.GetCookie("user") + assert.Equal(t, cookie, "gin") } // Tests that the response is serialized as JSON From fe49f0b616d48daa315c6a35323281c4fb95229a Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Fri, 2 Oct 2015 12:39:25 +0200 Subject: [PATCH 16/17] Fix exported test function --- context_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context_test.go b/context_test.go index 3deecadb..8232af98 100644 --- a/context_test.go +++ b/context_test.go @@ -239,14 +239,14 @@ func TestContextPostFormMultipart(t *testing.T) { } func TestContextSetCookie(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.SetCookie("user", "gin", 1, "/", "localhost", true, true) c.Request, _ = http.NewRequest("GET", "/set", nil) assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") } func TestContextGetCookie(t *testing.T) { - c, _, _ := createTestContext() + c, _, _ := CreateTestContext() c.Request, _ = http.NewRequest("GET", "/get", nil) c.Request.Header.Set("Cookie", "user=gin") cookie, _ := c.GetCookie("user") From 3d6390ed2bf3824b53841a92938639ea9b844e0c Mon Sep 17 00:00:00 2001 From: shadrus Date: Mon, 5 Oct 2015 21:59:22 +0300 Subject: [PATCH 17/17] Wrong copy/paste TODO --- logger_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/logger_test.go b/logger_test.go index dc6ba64b..c1471fe9 100644 --- a/logger_test.go +++ b/logger_test.go @@ -12,12 +12,6 @@ import ( "github.com/stretchr/testify/assert" ) -//TODO -// func (engine *Engine) LoadHTMLGlob(pattern string) { -// func (engine *Engine) LoadHTMLFiles(files ...string) { -// func (engine *Engine) Run(addr string) error { -// func (engine *Engine) RunTLS(addr string, cert string, key string) error { - func init() { SetMode(TestMode) }