mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-22 17:42:14 +08:00
fix conflict
This commit is contained in:
commit
c6a11b1941
18
README.md
18
README.md
@ -530,7 +530,7 @@ func main() {
|
|||||||
|
|
||||||
### Model binding and validation
|
### Model binding and validation
|
||||||
|
|
||||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
|
||||||
|
|
||||||
Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).
|
Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).
|
||||||
|
|
||||||
@ -538,10 +538,10 @@ Note that you need to set the corresponding binding tag on all fields you want t
|
|||||||
|
|
||||||
Also, Gin provides two sets of methods for binding:
|
Also, Gin provides two sets of methods for binding:
|
||||||
- **Type** - Must bind
|
- **Type** - Must bind
|
||||||
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`
|
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`
|
||||||
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
|
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
|
||||||
- **Type** - Should bind
|
- **Type** - Should bind
|
||||||
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`
|
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`
|
||||||
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
|
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
|
||||||
|
|
||||||
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
|
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
|
||||||
@ -859,12 +859,12 @@ form.html
|
|||||||
<form action="/" method="POST">
|
<form action="/" method="POST">
|
||||||
<p>Check some colors</p>
|
<p>Check some colors</p>
|
||||||
<label for="red">Red</label>
|
<label for="red">Red</label>
|
||||||
<input type="checkbox" name="colors[]" value="red" id="red" />
|
<input type="checkbox" name="colors[]" value="red" id="red">
|
||||||
<label for="green">Green</label>
|
<label for="green">Green</label>
|
||||||
<input type="checkbox" name="colors[]" value="green" id="green" />
|
<input type="checkbox" name="colors[]" value="green" id="green">
|
||||||
<label for="blue">Blue</label>
|
<label for="blue">Blue</label>
|
||||||
<input type="checkbox" name="colors[]" value="blue" id="blue" />
|
<input type="checkbox" name="colors[]" value="blue" id="blue">
|
||||||
<input type="submit" />
|
<input type="submit">
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1057,7 +1057,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// listen and serve on 0.0.0.0:8080
|
// listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080)
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1195,7 +1195,7 @@ You may use custom delims
|
|||||||
```go
|
```go
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
r.Delims("{[{", "}]}")
|
r.Delims("{[{", "}]}")
|
||||||
r.LoadHTMLGlob("/path/to/templates"))
|
r.LoadHTMLGlob("/path/to/templates")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Custom Template Funcs
|
#### Custom Template Funcs
|
||||||
|
@ -18,6 +18,7 @@ const (
|
|||||||
MIMEPROTOBUF = "application/x-protobuf"
|
MIMEPROTOBUF = "application/x-protobuf"
|
||||||
MIMEMSGPACK = "application/x-msgpack"
|
MIMEMSGPACK = "application/x-msgpack"
|
||||||
MIMEMSGPACK2 = "application/msgpack"
|
MIMEMSGPACK2 = "application/msgpack"
|
||||||
|
MIMEYAML = "application/x-yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Binding describes the interface which needs to be implemented for binding the
|
// Binding describes the interface which needs to be implemented for binding the
|
||||||
@ -44,7 +45,7 @@ type BindingUri interface {
|
|||||||
|
|
||||||
// StructValidator is the minimal interface which needs to be implemented in
|
// StructValidator is the minimal interface which needs to be implemented in
|
||||||
// order for it to be used as the validator engine for ensuring the correctness
|
// order for it to be used as the validator engine for ensuring the correctness
|
||||||
// of the reqest. Gin provides a default implementation for this using
|
// of the request. Gin provides a default implementation for this using
|
||||||
// https://github.com/go-playground/validator/tree/v8.18.2.
|
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||||
type StructValidator interface {
|
type StructValidator interface {
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
@ -75,6 +76,7 @@ var (
|
|||||||
FormMultipart = formMultipartBinding{}
|
FormMultipart = formMultipartBinding{}
|
||||||
ProtoBuf = protobufBinding{}
|
ProtoBuf = protobufBinding{}
|
||||||
MsgPack = msgpackBinding{}
|
MsgPack = msgpackBinding{}
|
||||||
|
YAML = yamlBinding{}
|
||||||
Uri = uriBinding{}
|
Uri = uriBinding{}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -94,6 +96,8 @@ func Default(method, contentType string) Binding {
|
|||||||
return ProtoBuf
|
return ProtoBuf
|
||||||
case MIMEMSGPACK, MIMEMSGPACK2:
|
case MIMEMSGPACK, MIMEMSGPACK2:
|
||||||
return MsgPack
|
return MsgPack
|
||||||
|
case MIMEYAML:
|
||||||
|
return YAML
|
||||||
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
||||||
return Form
|
return Form
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,12 @@ func TestBindingBody(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "JSON bidning",
|
name: "JSON binding",
|
||||||
binding: JSON,
|
binding: JSON,
|
||||||
body: `{"foo":"FOO"}`,
|
body: `{"foo":"FOO"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "XML bidning",
|
name: "XML binding",
|
||||||
binding: XML,
|
binding: XML,
|
||||||
body: `<?xml version="1.0" encoding="UTF-8"?>
|
body: `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<root>
|
<root>
|
||||||
@ -36,6 +36,11 @@ func TestBindingBody(t *testing.T) {
|
|||||||
binding: MsgPack,
|
binding: MsgPack,
|
||||||
body: msgPackBody(t),
|
body: msgPackBody(t),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "YAML binding",
|
||||||
|
binding: YAML,
|
||||||
|
body: `foo: FOO`,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Logf("testing: %s", tt.name)
|
t.Logf("testing: %s", tt.name)
|
||||||
req := requestWithBody("POST", "/", tt.body)
|
req := requestWithBody("POST", "/", tt.body)
|
||||||
|
@ -190,6 +190,9 @@ func TestBindingDefault(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
|
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
|
||||||
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
|
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
|
||||||
|
|
||||||
|
assert.Equal(t, YAML, Default("POST", MIMEYAML))
|
||||||
|
assert.Equal(t, YAML, Default("PUT", MIMEYAML))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBindingJSON(t *testing.T) {
|
func TestBindingJSON(t *testing.T) {
|
||||||
@ -473,6 +476,20 @@ func TestBindingXMLFail(t *testing.T) {
|
|||||||
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
|
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindingYAML(t *testing.T) {
|
||||||
|
testBodyBinding(t,
|
||||||
|
YAML, "yaml",
|
||||||
|
"/", "/",
|
||||||
|
`foo: bar`, `bar: foo`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingYAMLFail(t *testing.T) {
|
||||||
|
testBodyBindingFail(t,
|
||||||
|
YAML, "yaml",
|
||||||
|
"/", "/",
|
||||||
|
`foo:\nbar`, `bar: foo`)
|
||||||
|
}
|
||||||
|
|
||||||
func createFormPostRequest() *http.Request {
|
func createFormPostRequest() *http.Request {
|
||||||
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
|
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
|
||||||
req.Header.Set("Content-Type", MIMEPOSTForm)
|
req.Header.Set("Content-Type", MIMEPOSTForm)
|
||||||
|
@ -29,7 +29,7 @@ func (protobufBinding) BindBody(body []byte, obj interface{}) error {
|
|||||||
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Here it's same to return validate(obj), but util now we cann't add
|
// Here it's same to return validate(obj), but util now we can't add
|
||||||
// `binding:""` to the struct which automatically generate by gen-proto
|
// `binding:""` to the struct which automatically generate by gen-proto
|
||||||
return nil
|
return nil
|
||||||
// return validate(obj)
|
// return validate(obj)
|
||||||
|
35
binding/yaml.go
Normal file
35
binding/yaml.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2018 Gin Core Team. 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 (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type yamlBinding struct{}
|
||||||
|
|
||||||
|
func (yamlBinding) Name() string {
|
||||||
|
return "yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
return decodeYAML(req.Body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return decodeYAML(bytes.NewReader(body), obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeYAML(r io.Reader, obj interface{}) error {
|
||||||
|
decoder := yaml.NewDecoder(r)
|
||||||
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
13
context.go
13
context.go
@ -31,6 +31,7 @@ const (
|
|||||||
MIMEPlain = binding.MIMEPlain
|
MIMEPlain = binding.MIMEPlain
|
||||||
MIMEPOSTForm = binding.MIMEPOSTForm
|
MIMEPOSTForm = binding.MIMEPOSTForm
|
||||||
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
||||||
|
MIMEYAML = binding.MIMEYAML
|
||||||
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
|
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -524,8 +525,13 @@ func (c *Context) BindQuery(obj interface{}) error {
|
|||||||
return c.MustBindWith(obj, binding.Query)
|
return c.MustBindWith(obj, binding.Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
|
||||||
|
func (c *Context) BindYAML(obj interface{}) error {
|
||||||
|
return c.MustBindWith(obj, binding.YAML)
|
||||||
|
}
|
||||||
|
|
||||||
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// It will abort the request with HTTP 400 if any error ocurrs.
|
// It will abort the request with HTTP 400 if any error occurs.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
||||||
if err = c.ShouldBindWith(obj, b); err != nil {
|
if err = c.ShouldBindWith(obj, b); err != nil {
|
||||||
@ -563,6 +569,11 @@ func (c *Context) ShouldBindQuery(obj interface{}) error {
|
|||||||
return c.ShouldBindWith(obj, binding.Query)
|
return c.ShouldBindWith(obj, binding.Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
|
||||||
|
func (c *Context) ShouldBindYAML(obj interface{}) error {
|
||||||
|
return c.ShouldBindWith(obj, binding.YAML)
|
||||||
|
}
|
||||||
|
|
||||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||||
func (c *Context) ShouldBindUri(obj interface{}) error {
|
func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||||
m := make(map[string][]string)
|
m := make(map[string][]string)
|
||||||
|
@ -1380,6 +1380,23 @@ func TestContextBindWithQuery(t *testing.T) {
|
|||||||
assert.Equal(t, 0, w.Body.Len())
|
assert.Equal(t, 0, w.Body.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextBindWithYAML(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
||||||
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
|
var obj struct {
|
||||||
|
Foo string `yaml:"foo"`
|
||||||
|
Bar string `yaml:"bar"`
|
||||||
|
}
|
||||||
|
assert.NoError(t, c.BindYAML(&obj))
|
||||||
|
assert.Equal(t, "foo", obj.Bar)
|
||||||
|
assert.Equal(t, "bar", obj.Foo)
|
||||||
|
assert.Equal(t, 0, w.Body.Len())
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextBadAutoBind(t *testing.T) {
|
func TestContextBadAutoBind(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
@ -1470,6 +1487,23 @@ func TestContextShouldBindWithQuery(t *testing.T) {
|
|||||||
assert.Equal(t, 0, w.Body.Len())
|
assert.Equal(t, 0, w.Body.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextShouldBindWithYAML(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
|
||||||
|
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
|
||||||
|
|
||||||
|
var obj struct {
|
||||||
|
Foo string `yaml:"foo"`
|
||||||
|
Bar string `yaml:"bar"`
|
||||||
|
}
|
||||||
|
assert.NoError(t, c.ShouldBindYAML(&obj))
|
||||||
|
assert.Equal(t, "foo", obj.Bar)
|
||||||
|
assert.Equal(t, "bar", obj.Foo)
|
||||||
|
assert.Equal(t, 0, w.Body.Len())
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextBadAutoShouldBind(t *testing.T) {
|
func TestContextBadAutoShouldBind(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
The middleware has two parts:
|
The middleware has two parts:
|
||||||
|
|
||||||
- part one is what is executed once, when you initalize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime.
|
- part one is what is executed once, when you initialize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime.
|
||||||
|
|
||||||
- part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler furnction.
|
- part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler function.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func funcName(params string) gin.HandlerFunc {
|
func funcName(params string) gin.HandlerFunc {
|
||||||
|
@ -24,9 +24,10 @@ const (
|
|||||||
ErrorTypePrivate ErrorType = 1 << 0
|
ErrorTypePrivate ErrorType = 1 << 0
|
||||||
// ErrorTypePublic indicates a public error.
|
// ErrorTypePublic indicates a public error.
|
||||||
ErrorTypePublic ErrorType = 1 << 1
|
ErrorTypePublic ErrorType = 1 << 1
|
||||||
// ErrorTypeAny indicates other any error.
|
// ErrorTypeAny indicates any other error.
|
||||||
ErrorTypeAny ErrorType = 1<<64 - 1
|
ErrorTypeAny ErrorType = 1<<64 - 1
|
||||||
ErrorTypeNu = 2
|
// ErrorTypeNu indicates any other error.
|
||||||
|
ErrorTypeNu = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents a error's specification.
|
// Error represents a error's specification.
|
||||||
@ -52,6 +53,7 @@ func (msg *Error) SetMeta(data interface{}) *Error {
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSON creates a properly formated JSON
|
||||||
func (msg *Error) JSON() interface{} {
|
func (msg *Error) JSON() interface{} {
|
||||||
json := H{}
|
json := H{}
|
||||||
if msg.Meta != nil {
|
if msg.Meta != nil {
|
||||||
|
@ -19,7 +19,7 @@ func main() {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
client := pb.NewGreeterClient(conn)
|
client := pb.NewGreeterClient(conn)
|
||||||
|
|
||||||
// Set up a http setver.
|
// Set up a http server.
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
r.GET("/rest/n/:name", func(c *gin.Context) {
|
r.GET("/rest/n/:name", func(c *gin.Context) {
|
||||||
name := c.Param("name")
|
name := c.Param("name")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
@ -20,9 +20,9 @@
|
|||||||
<!-- Latest compiled and minified JavaScript -->
|
<!-- Latest compiled and minified JavaScript -->
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
||||||
<!-- Primjs -->
|
<!-- Primjs -->
|
||||||
<link href="/static/prismjs.min.css" rel="stylesheet" />
|
<link href="/static/prismjs.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
StartRealtime({{.roomid}}, {{.timestamp}});
|
StartRealtime({{.roomid}}, {{.timestamp}});
|
||||||
});
|
});
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
|
<li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
|
||||||
<li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
|
<li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
|
||||||
<li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
|
<li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
|
||||||
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">Github</a></li>
|
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">GitHub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- /.nav-collapse -->
|
</div><!-- /.nav-collapse -->
|
||||||
</div><!-- /.container -->
|
</div><!-- /.container -->
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Server-Sent Events in Go</h1>
|
<h1>Server-Sent Events in Go</h1>
|
||||||
<p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
|
<p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
|
||||||
<p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
|
<p>The chat and the charts data is provided in realtime using the SSE implementation of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:290px">
|
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:290px">
|
||||||
@ -79,19 +79,19 @@
|
|||||||
<label class="sr-only" for="chat-message">Message</label>
|
<label class="sr-only" for="chat-message">Message</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-addon">{{.nick}}</div>
|
<div class="input-group-addon">{{.nick}}</div>
|
||||||
<input type="text" name="message" id="chat-message" class="form-control" placeholder="a message" value="" />
|
<input type="text" name="message" id="chat-message" class="form-control" placeholder="a message" value="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" class="btn btn-primary" value="Send" />
|
<input type="submit" class="btn btn-primary" value="Send">
|
||||||
</form>
|
</form>
|
||||||
{{else}}
|
{{else}}
|
||||||
<form action="" method="get" class="form-inline">
|
<form action="" method="get" class="form-inline">
|
||||||
<legend>Join the SSE real-time chat</legend>
|
<legend>Join the SSE real-time chat</legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input value='' name="nick" id="nick" placeholder="Your Name" type="text" class="form-control" />
|
<input value='' name="nick" id="nick" placeholder="Your Name" type="text" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group text-center">
|
<div class="form-group text-center">
|
||||||
<input type="submit" class="btn btn-success btn-login-submit" value="Join" />
|
<input type="submit" class="btn btn-success btn-login-submit" value="Join">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -6,7 +6,7 @@ var html = template.Must(template.New("chat_room").Parse(`
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>{{.roomid}}</title>
|
<title>{{.roomid}}</title>
|
||||||
<link rel="stylesheet" type="text/css" href="http://meyerweb.com/eric/tools/css/reset/reset.css"/>
|
<link rel="stylesheet" type="text/css" href="http://meyerweb.com/eric/tools/css/reset/reset.css">
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
|
||||||
<script src="http://malsup.github.com/jquery.form.js"></script>
|
<script src="http://malsup.github.com/jquery.form.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@ -35,9 +35,9 @@ var html = template.Must(template.New("chat_room").Parse(`
|
|||||||
<h1>Welcome to {{.roomid}} room</h1>
|
<h1>Welcome to {{.roomid}} room</h1>
|
||||||
<div id="messages"></div>
|
<div id="messages"></div>
|
||||||
<form id="myForm" action="/room/{{.roomid}}" method="post">
|
<form id="myForm" action="/room/{{.roomid}}" method="post">
|
||||||
User: <input id="user_form" name="user" value="{{.userid}}"></input>
|
User: <input id="user_form" name="user" value="{{.userid}}">
|
||||||
Message: <input id="message_form" name="message"></input>
|
Message: <input id="message_form" name="message">
|
||||||
<input type="submit" value="Submit" />
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
10
ginS/gins.go
10
ginS/gins.go
@ -47,8 +47,8 @@ func NoMethod(handlers ...gin.HandlerFunc) {
|
|||||||
engine().NoMethod(handlers...)
|
engine().NoMethod(handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
|
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
|
||||||
// For example, all the routes that use a common middlware for authorization could be grouped.
|
// For example, all the routes that use a common middleware for authorization could be grouped.
|
||||||
func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
|
func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
|
||||||
return engine().Group(relativePath, handlers...)
|
return engine().Group(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
@ -127,21 +127,21 @@ func Use(middlewares ...gin.HandlerFunc) gin.IRoutes {
|
|||||||
|
|
||||||
// Run : The router is attached to a http.Server and starts listening and serving HTTP requests.
|
// Run : The router is attached to a http.Server and starts listening and serving HTTP requests.
|
||||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
// It is a shortcut for http.ListenAndServe(addr, router)
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
func Run(addr ...string) (err error) {
|
func Run(addr ...string) (err error) {
|
||||||
return engine().Run(addr...)
|
return engine().Run(addr...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunTLS : The router is attached to a http.Server and starts listening and serving HTTPS requests.
|
// RunTLS : 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)
|
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
func RunTLS(addr, certFile, keyFile string) (err error) {
|
func RunTLS(addr, certFile, keyFile string) (err error) {
|
||||||
return engine().RunTLS(addr, certFile, keyFile)
|
return engine().RunTLS(addr, certFile, keyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunUnix : The router is attached to a http.Server and starts listening and serving HTTP requests
|
// RunUnix : The router is attached to a http.Server and starts listening and serving HTTP requests
|
||||||
// through the specified unix socket (ie. a file)
|
// through the specified unix socket (ie. a file)
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
func RunUnix(file string) (err error) {
|
func RunUnix(file string) (err error) {
|
||||||
return engine().RunUnix(file)
|
return engine().RunUnix(file)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func Logger() HandlerFunc {
|
|||||||
return LoggerWithWriter(DefaultWriter)
|
return LoggerWithWriter(DefaultWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
// LoggerWithWriter instance a Logger middleware with the specified writer buffer.
|
||||||
// Example: os.Stdout, a file opened in write mode, a socket...
|
// Example: os.Stdout, a file opened in write mode, a socket...
|
||||||
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
isTerm := true
|
isTerm := true
|
||||||
|
4
mode.go
4
mode.go
@ -28,7 +28,7 @@ const (
|
|||||||
testCode
|
testCode
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultWriter is the default io.Writer used the Gin for debug output and
|
// DefaultWriter is the default io.Writer used by Gin for debug output and
|
||||||
// middleware output like Logger() or Recovery().
|
// middleware output like Logger() or Recovery().
|
||||||
// Note that both Logger and Recovery provides custom ways to configure their
|
// Note that both Logger and Recovery provides custom ways to configure their
|
||||||
// output io.Writer.
|
// output io.Writer.
|
||||||
@ -36,6 +36,8 @@ const (
|
|||||||
// import "github.com/mattn/go-colorable"
|
// import "github.com/mattn/go-colorable"
|
||||||
// gin.DefaultWriter = colorable.NewColorableStdout()
|
// gin.DefaultWriter = colorable.NewColorableStdout()
|
||||||
var DefaultWriter io.Writer = os.Stdout
|
var DefaultWriter io.Writer = os.Stdout
|
||||||
|
|
||||||
|
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
|
||||||
var DefaultErrorWriter io.Writer = os.Stderr
|
var DefaultErrorWriter io.Writer = os.Stderr
|
||||||
|
|
||||||
var ginMode = debugCode
|
var ginMode = debugCode
|
||||||
|
@ -53,8 +53,8 @@ func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
|
|||||||
return group.returnObj()
|
return group.returnObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
|
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
|
||||||
// For example, all the routes that use a common middlware for authorization could be grouped.
|
// For example, all the routes that use a common middleware for authorization could be grouped.
|
||||||
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
||||||
return &RouterGroup{
|
return &RouterGroup{
|
||||||
Handlers: group.combineHandlers(handlers),
|
Handlers: group.combineHandlers(handlers),
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Used as a workaround since we can't compare functions or their addressses
|
// Used as a workaround since we can't compare functions or their addresses
|
||||||
var fakeHandlerValue string
|
var fakeHandlerValue string
|
||||||
|
|
||||||
func fakeHandler(val string) HandlersChain {
|
func fakeHandler(val string) HandlersChain {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user