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;