From 83c4b7f52d167aed7cce087b0fc78d6adef07b3f Mon Sep 17 00:00:00 2001 From: Gerasimos Maropoulos Date: Thu, 8 Mar 2018 05:21:16 +0200 Subject: [PATCH] add examples for read using custom decoder per type, read using custom decoder via `iris#UnmarshalerFunc` and to complete it add an example for the `context#ReadXML`. Former-commit-id: 536b1780f12d0b9d9ce9aa976a0f95f18634ec2d --- _examples/README.md | 7 +- .../http_request/read-custom-per-type/main.go | 65 +++++++++++++++++ .../read-custom-per-type/main_test.go | 17 +++++ .../read-custom-via-unmarshaler/main.go | 73 +++++++++++++++++++ .../read-custom-via-unmarshaler/main_test.go | 17 +++++ _examples/http_request/read-json/main.go | 14 ++-- _examples/http_request/read-xml/main.go | 50 +++++++++++++ _examples/http_request/read-xml/main_test.go | 18 +++++ .../http_responsewriter/herotemplate/app.go | 36 ++++----- _examples/tutorial/online-visitors/main.go | 8 +- context/context.go | 29 ++++++++ core/memstore/memstore.go | 2 +- doc.go | 2 +- go19.go | 10 +++ 14 files changed, 314 insertions(+), 34 deletions(-) create mode 100644 _examples/http_request/read-custom-per-type/main.go create mode 100644 _examples/http_request/read-custom-per-type/main_test.go create mode 100644 _examples/http_request/read-custom-via-unmarshaler/main.go create mode 100644 _examples/http_request/read-custom-via-unmarshaler/main_test.go create mode 100644 _examples/http_request/read-xml/main.go create mode 100644 _examples/http_request/read-xml/main_test.go diff --git a/_examples/README.md b/_examples/README.md index 2ba8c680..a53b4998 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -318,8 +318,11 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her ### How to Read from `context.Request() *http.Request` -- [Bind JSON](http_request/read-json/main.go) -- [Bind Form](http_request/read-form/main.go) +- [Read JSON](http_request/read-json/main.go) +- [Read XML](http_request/read-xml/main.go) +- [Read Form](http_request/read-form/main.go) +- [Read Custom per type](http_request/read-custom-per-type/main.go) +- [Read Custom via Unmarshaler](http_request/read-custom-via-unmarshaler/main.go) - [Upload/Read File](http_request/upload-file/main.go) - [Upload multiple files with an easy way](http_request/upload-files/main.go) diff --git a/_examples/http_request/read-custom-per-type/main.go b/_examples/http_request/read-custom-per-type/main.go new file mode 100644 index 00000000..2c339988 --- /dev/null +++ b/_examples/http_request/read-custom-per-type/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "gopkg.in/yaml.v2" + + "github.com/kataras/iris" +) + +func main() { + app := newApp() + + // use Postman or whatever to do a POST request + // (however you are always free to use app.Get and GET http method requests to read body of course) + // to the http://localhost:8080 with RAW BODY: + /* + addr: localhost:8080 + serverName: Iris + */ + // + // The response should be: + // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} + app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) +} + +func newApp() *iris.Application { + app := iris.New() + app.Post("/", handler) + + return app +} + +// simple yaml stuff, read more at https://github.com/go-yaml/yaml +type config struct { + Addr string `yaml:"addr"` + ServerName string `yaml:"serverName"` +} + +// Decode implements the `kataras/iris/context#BodyDecoder` optional interface +// that any go type can implement in order to be self-decoded when reading the request's body. +func (c *config) Decode(body []byte) error { + return yaml.Unmarshal(body, c) +} + +func handler(ctx iris.Context) { + var c config + + // + // Note: + // second parameter is nil because our &c implements the `context#BodyDecoder` + // which has a priority over the context#Unmarshaler (which can be a more global option for reading request's body) + // see the `http_request/read-custom-via-unmarshaler/main.go` example to learn how to use the context#Unmarshaler too. + // + // Note 2: + // If you need to read the body again for any reason + // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`. + // + + if err := ctx.UnmarshalBody(&c, nil); err != nil { + ctx.StatusCode(iris.StatusBadRequest) + ctx.WriteString(err.Error()) + return + } + + ctx.Writef("Received: %#+v", c) +} diff --git a/_examples/http_request/read-custom-per-type/main_test.go b/_examples/http_request/read-custom-per-type/main_test.go new file mode 100644 index 00000000..323b08b3 --- /dev/null +++ b/_examples/http_request/read-custom-per-type/main_test.go @@ -0,0 +1,17 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/httptest" +) + +func TestReadCustomPerType(t *testing.T) { + app := newApp() + e := httptest.New(t, app) + + expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}` + + e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect(). + Status(httptest.StatusOK).Body().Equal(expectedResponse) +} diff --git a/_examples/http_request/read-custom-via-unmarshaler/main.go b/_examples/http_request/read-custom-via-unmarshaler/main.go new file mode 100644 index 00000000..4c4bcf55 --- /dev/null +++ b/_examples/http_request/read-custom-via-unmarshaler/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "gopkg.in/yaml.v2" + + "github.com/kataras/iris" +) + +func main() { + app := newApp() + + // use Postman or whatever to do a POST request + // (however you are always free to use app.Get and GET http method requests to read body of course) + // to the http://localhost:8080 with RAW BODY: + /* + addr: localhost:8080 + serverName: Iris + */ + // + // The response should be: + // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} + app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) +} + +func newApp() *iris.Application { + app := iris.New() + app.Post("/", handler) + + return app +} + +// simple yaml stuff, read more at https://github.com/go-yaml/yaml +type config struct { + Addr string `yaml:"addr"` + ServerName string `yaml:"serverName"` +} + +/* +type myBodyDecoder struct{} + +var DefaultBodyDecoder = myBodyDecoder{} + +// Implements the `kataras/iris/context#Unmarshaler` but at our example +// we will use the simplest `context#UnmarshalerFunc` to pass just the yaml.Unmarshal. +// +// Can be used as: ctx.UnmarshalBody(&c, DefaultBodyDecoder) +func (r *myBodyDecoder) Unmarshal(data []byte, outPtr interface{}) error { + return yaml.Unmarshal(data, outPtr) +} +*/ + +func handler(ctx iris.Context) { + var c config + + // + // Note: + // yaml.Unmarshal already implements the `context#Unmarshaler` + // so we can use it directly, like the json.Unmarshal(ctx.ReadJSON), xml.Unmarshal(ctx.ReadXML) + // and every library which follows the best practises and is aligned with the Go standards. + // + // Note 2: + // If you need to read the body again for any reason + // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`. + // + + if err := ctx.UnmarshalBody(&c, iris.UnmarshalerFunc(yaml.Unmarshal)); err != nil { + ctx.StatusCode(iris.StatusBadRequest) + ctx.WriteString(err.Error()) + return + } + + ctx.Writef("Received: %#+v", c) +} diff --git a/_examples/http_request/read-custom-via-unmarshaler/main_test.go b/_examples/http_request/read-custom-via-unmarshaler/main_test.go new file mode 100644 index 00000000..a068a65d --- /dev/null +++ b/_examples/http_request/read-custom-via-unmarshaler/main_test.go @@ -0,0 +1,17 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/httptest" +) + +func TestReadCustomViaUnmarshaler(t *testing.T) { + app := newApp() + e := httptest.New(t, app) + + expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}` + + e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect(). + Status(httptest.StatusOK).Body().Equal(expectedResponse) +} diff --git a/_examples/http_request/read-json/main.go b/_examples/http_request/read-json/main.go index fb4ff396..88f84407 100644 --- a/_examples/http_request/read-json/main.go +++ b/_examples/http_request/read-json/main.go @@ -11,16 +11,18 @@ type Company struct { } func MyHandler(ctx iris.Context) { - c := &Company{} - if err := ctx.ReadJSON(c); err != nil { + var c Company + + if err := ctx.ReadJSON(&c); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.WriteString(err.Error()) return } - ctx.Writef("Received: %#v\n", c) + ctx.Writef("Received: %#+v\n", c) } +// simple json stuff, read more at https://golang.org/pkg/encoding/json type Person struct { Name string `json:"name"` Age int `json:"age"` @@ -55,9 +57,9 @@ func main() { "Other": "Something here" } */ - // and Content-Type to application/json + // and Content-Type to application/json (optionally but good practise) // // The response should be: - // Received: &main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} - app.Run(iris.Addr(":8080")) + // Received: main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} + app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) } diff --git a/_examples/http_request/read-xml/main.go b/_examples/http_request/read-xml/main.go new file mode 100644 index 00000000..5261bcfa --- /dev/null +++ b/_examples/http_request/read-xml/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "encoding/xml" + + "github.com/kataras/iris" +) + +func main() { + app := newApp() + + // use Postman or whatever to do a POST request + // to the http://localhost:8080 with RAW BODY: + /* + + Description of this person, the body of this inner element. + + */ + // and Content-Type to application/xml (optionally but good practise) + // + // The response should be: + // Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."} + app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) +} + +func newApp() *iris.Application { + app := iris.New() + app.Post("/", handler) + + return app +} + +// simple xml stuff, read more at https://golang.org/pkg/encoding/xml +type person struct { + XMLName xml.Name `xml:"person"` // element name + Name string `xml:"name,attr"` // ,attr for attribute. + Age int `xml:"age,attr"` // ,attr attribute. + Description string `xml:"description"` // inner element name, value is its body. +} + +func handler(ctx iris.Context) { + var p person + if err := ctx.ReadXML(&p); err != nil { + ctx.StatusCode(iris.StatusBadRequest) + ctx.WriteString(err.Error()) + return + } + + ctx.Writef("Received: %#+v", p) +} diff --git a/_examples/http_request/read-xml/main_test.go b/_examples/http_request/read-xml/main_test.go new file mode 100644 index 00000000..da4fbd7a --- /dev/null +++ b/_examples/http_request/read-xml/main_test.go @@ -0,0 +1,18 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/httptest" +) + +func TestReadXML(t *testing.T) { + app := newApp() + e := httptest.New(t, app) + + expectedResponse := `Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."}` + send := `Description of this person, the body of this inner element.` + + e.POST("/").WithText(send).Expect(). + Status(httptest.StatusOK).Body().Equal(expectedResponse) +} diff --git a/_examples/http_responsewriter/herotemplate/app.go b/_examples/http_responsewriter/herotemplate/app.go index 4f1412ac..f948f71d 100644 --- a/_examples/http_responsewriter/herotemplate/app.go +++ b/_examples/http_responsewriter/herotemplate/app.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "log" "github.com/kataras/iris/_examples/http_responsewriter/herotemplate/template" @@ -13,11 +12,15 @@ import ( // $ go run app.go // // Read more at https://github.com/shiyanhui/hero/hero + func main() { app := iris.New() app.Get("/users", func(ctx iris.Context) { + ctx.Gzip(true) + ctx.ContentType("text/html") + var userList = []string{ "Alice", "Bob", @@ -25,30 +28,27 @@ func main() { } // Had better use buffer sync.Pool. - // Hero exports GetBuffer and PutBuffer for this. + // Hero(github.com/shiyanhui/hero/hero) exports GetBuffer and PutBuffer for this. // // buffer := hero.GetBuffer() // defer hero.PutBuffer(buffer) - buffer := new(bytes.Buffer) - template.UserList(userList, buffer) - - if _, err := ctx.Write(buffer.Bytes()); err != nil { - log.Printf("ERR: %s\n", err) - } - }) - - app.Get("/users2", func(ctx iris.Context) { - var userList = []string{ - "Alice", - "Bob", - "Tom", - } + // buffer := new(bytes.Buffer) + // template.UserList(userList, buffer) + // ctx.Write(buffer.Bytes()) // using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool), // iris context implements the io.Writer by its ResponseWriter // which is an enhanced version of the standard http.ResponseWriter - // but still 100% compatible. - template.UserListToWriter(userList, ctx) + // but still 100% compatible, GzipResponseWriter too: + // _, err := template.UserListToWriter(userList, ctx.GzipResponseWriter()) + buffer := new(bytes.Buffer) + template.UserList(userList, buffer) + + _, err := ctx.Write(buffer.Bytes()) + if err != nil { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.WriteString(err.Error()) + } }) app.Run(iris.Addr(":8080")) diff --git a/_examples/tutorial/online-visitors/main.go b/_examples/tutorial/online-visitors/main.go index 023bda63..e6f3fa11 100644 --- a/_examples/tutorial/online-visitors/main.go +++ b/_examples/tutorial/online-visitors/main.go @@ -58,15 +58,11 @@ func (v *pageView) increment() { } func (v *pageView) decrement() { - oldCount := v.count - if oldCount > 0 { - atomic.StoreUint64(&v.count, oldCount-1) - } + atomic.AddUint64(&v.count, ^uint64(0)) } func (v *pageView) getCount() uint64 { - val := atomic.LoadUint64(&v.count) - return val + return atomic.LoadUint64(&v.count) } type ( diff --git a/context/context.go b/context/context.go index b5001587..e083306f 100644 --- a/context/context.go +++ b/context/context.go @@ -48,6 +48,8 @@ type ( // // Note: This is totally optionally, the default decoders // for ReadJSON is the encoding/json and for ReadXML is the encoding/xml. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-per-type/main.go BodyDecoder interface { Decode(data []byte) error } @@ -61,6 +63,8 @@ type ( // UnmarshalerFunc a shortcut for the Unmarshaler interface // // See 'Unmarshaler' and 'BodyDecoder' for more. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go UnmarshalerFunc func(data []byte, outPtr interface{}) error ) @@ -563,6 +567,8 @@ type Context interface { // // The default form's memory maximum size is 32MB, it can be changed by the // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument. + // + // Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file FormFile(key string) (multipart.File, *multipart.FileHeader, error) // UploadFormFiles uploads any received file(s) from the client // to the system physical location "destDirectory". @@ -587,6 +593,9 @@ type Context interface { // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument. // // See `FormFile` to a more controlled to receive a file. + // + // + // Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) // +------------------------------------------------------------+ @@ -611,13 +620,21 @@ type Context interface { // UnmarshalBody reads the request's body and binds it to a value or pointer of any type. // Examples of usage: context.ReadJSON, context.ReadXML. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error // ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go ReadJSON(jsonObjectPtr interface{}) error // ReadXML reads XML from request's body and binds it to a pointer of a value of any xml-valid type. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go ReadXML(xmlObjectPtr interface{}) error // ReadForm binds the formObject with the form data // it supports any kind of struct. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go ReadForm(formObjectPtr interface{}) error // +------------------------------------------------------------+ @@ -1932,6 +1949,8 @@ func (ctx *context) PostValues(name string) []string { // // The default form's memory maximum size is 32MB, it can be changed by the // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument. +// +// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { // we don't have access to see if the request is body stream // and then the ParseMultipartForm can be useless @@ -1965,6 +1984,8 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument. // // See `FormFile` to a more controlled to receive a file. +// +// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files func (ctx *context) UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) { err = ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()) if err != nil { @@ -2056,6 +2077,8 @@ func (ctx *context) SetMaxRequestBodySize(limitOverBytes int64) { // UnmarshalBody reads the request's body and binds it to a value or pointer of any type // Examples of usage: context.ReadJSON, context.ReadXML. +// +// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go func (ctx *context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error { if ctx.request.Body == nil { return errors.New("unmarshal: empty body") @@ -2096,6 +2119,8 @@ func (ctx *context) shouldOptimize() bool { } // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type. +// +// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go func (ctx *context) ReadJSON(jsonObject interface{}) error { var unmarshaler = json.Unmarshal if ctx.shouldOptimize() { @@ -2105,6 +2130,8 @@ func (ctx *context) ReadJSON(jsonObject interface{}) error { } // ReadXML reads XML from request's body and binds it to a value of any xml-valid type. +// +// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go func (ctx *context) ReadXML(xmlObject interface{}) error { return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal)) } @@ -2115,6 +2142,8 @@ var ( // ReadForm binds the formObject with the form data // it supports any kind of struct. +// +// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go func (ctx *context) ReadForm(formObject interface{}) error { values := ctx.FormValues() if values == nil { diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go index 2e161560..0beec0e6 100644 --- a/core/memstore/memstore.go +++ b/core/memstore/memstore.go @@ -180,7 +180,7 @@ func (e Entry) BoolDefault(def bool) (bool, error) { // respects the immutable. func (e Entry) Value() interface{} { if e.immutable { - // take its value, no pointer even if setted with a rreference. + // take its value, no pointer even if setted with a reference. vv := reflect.Indirect(reflect.ValueOf(e.ValueRaw)) // return copy of that slice diff --git a/doc.go b/doc.go index b9eab9be..e4bd5942 100644 --- a/doc.go +++ b/doc.go @@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub: Current Version -10.0.0 +10.2.1 Installation diff --git a/go19.go b/go19.go index 8a16665e..adcd8a4f 100644 --- a/go19.go +++ b/go19.go @@ -17,6 +17,16 @@ type ( // Developers send responses to the client's request through a Context. // Developers get request information from the client's request by a Context. Context = context.Context + // UnmarshalerFunc a shortcut, an alias for the `context#UnmarshalerFunc` type + // which implements the `context#Unmarshaler` interface for reading request's body + // via custom decoders, most of them already implement the `context#UnmarshalerFunc` + // like the json.Unmarshal, xml.Unmarshal, yaml.Unmarshal and every library which + // follows the best practises and is aligned with the Go standards. + // + // See 'context#UnmarshalBody` for more. + // + // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go + UnmarshalerFunc = context.UnmarshalerFunc // A Handler responds to an HTTP request. // It writes reply headers and data to the Context.ResponseWriter() and then return. // Returning signals that the request is finished;