diff --git a/HISTORY.md b/HISTORY.md index 55d718f8..3b894a05 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,25 @@ **How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`. +## 6.1.2 -> 6.1.3 + +- Added a configuration field `iris.Config.DisableBodyConsumptionOnUnmarshal` + +```go +// DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders. +// If setted to true then it +// disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`. +// +// By-default io.ReadAll` is used to read the body from the `context.Request.Body which is an `io.ReadCloser`, +// if this field setted to true then a new buffer will be created to read from and the request body. +// The body will not be changed and existing data before the context.UnmarshalBody/ReadJSON/ReadXML will be not consumed. +DisableBodyConsumptionOnUnmarshal bool +``` + +If that option is setted to true then you can read more than one times from the same `context.Request.Body`. +Defaults to false because the majority of developers expecting request body to be empty after unmarshal. + + ## 6.1.1 -> 6.1.2 Better internalization and localization support, with ability to change the cookie's key and context's keys. diff --git a/README.md b/README.md index 0f3618e3..b88310e3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- Logo created by an Iris community member, @OneebMalik @@ -20,7 +20,7 @@
-CHANGELOG/HISTORY +CHANGELOG/HISTORY Examples @@ -68,7 +68,7 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/go-template/html" -) +) func main(){ @@ -83,34 +83,34 @@ func main(){ // // Use the html standard engine for all files inside "./views" folder with extension ".html" iris.UseTemplate(html.New()).Directory("./views", ".html") - + // http://localhost:6111 // Method: "GET" // Render ./views/index.html iris.Get("/", func(ctx *iris.Context){ ctx.Render("index.html", nil) }) - - // Group routes, optionally: share middleware, template layout and custom http errors. + + // Group routes, optionally: share middleware, template layout and custom http errors. userAPI := iris.Party("/users", userAPIMiddleware). Layout("layouts/userLayout.html") { - // Fire userNotFoundHandler when Not Found + // Fire userNotFoundHandler when Not Found // inside http://localhost:6111/users/*anything userAPI.OnError(404, userNotFoundHandler) - + // http://localhost:6111/users // Method: "GET" userAPI.Get("/", getAllHandler) - + // http://localhost:6111/users/42 // Method: "GET" userAPI.Get("/:id", getByIDHandler) - + // http://localhost:6111/users // Method: "POST" userAPI.Post("/", saveUserHandler) - } + } getByIDHandler := func(ctx *iris.Context){ // take the :id from the path, parse to integer @@ -128,7 +128,7 @@ func main(){ // like the iris.Map{"username" : user.Username}. ctx.JSON(iris.StatusOK, user) } - + // Start the server at 0.0.0.0:6111 iris.Listen(":6111") } @@ -209,7 +209,7 @@ Besides the fact that we have a [community chat][Chat] for questions or reports Versioning ------------ -Current: **v6.1.2** +Current: **v6.1.3** v5: https://github.com/kataras/iris/tree/5.0.0 diff --git a/configuration.go b/configuration.go index df2588fa..84c25d7b 100644 --- a/configuration.go +++ b/configuration.go @@ -150,6 +150,15 @@ type Configuration struct { // Default is false DisableBanner bool + // DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders. + // If setted to true then it + // disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`. + // + // By-default io.ReadAll` is used to read the body from the `context.Request.Body which is an `io.ReadCloser`, + // if this field setted to true then a new buffer will be created to read from and the request body. + // The body will not be changed and existing data before the context.UnmarshalBody/ReadJSON/ReadXML will be not consumed. + DisableBodyConsumptionOnUnmarshal bool + // LoggerOut is the destination for output // // Default is os.Stdout @@ -349,6 +358,19 @@ var ( } } + // OptionDisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders. + // If setted to true then it + // disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`. + // + // By-default io.ReadAll` is used to read the body from the `context.Request.Body which is an `io.ReadCloser`, + // if this field setted to true then a new buffer will be created to read from and the request body. + // The body will not be changed and existing data before the context.UnmarshalBody/ReadJSON/ReadXML will be not consumed. + OptionDisableBodyConsumptionOnUnmarshal = func(val bool) OptionSet { + return func(c *Configuration) { + c.DisableBodyConsumptionOnUnmarshal = val + } + } + // OptionLoggerOut is the destination for output // // Default is os.Stdout @@ -461,27 +483,28 @@ var ( // DefaultConfiguration returns the default configuration for an Iris station, fills the main Configuration func DefaultConfiguration() Configuration { return Configuration{ - VHost: "", - VScheme: "", - ReadTimeout: DefaultReadTimeout, - WriteTimeout: DefaultWriteTimeout, - MaxHeaderBytes: DefaultMaxHeaderBytes, - CheckForUpdates: false, - CheckForUpdatesSync: false, - DisablePathCorrection: DefaultDisablePathCorrection, - EnablePathEscape: DefaultEnablePathEscape, - FireMethodNotAllowed: false, - DisableBanner: false, - LoggerOut: DefaultLoggerOut, - LoggerPreffix: DefaultLoggerPreffix, - DisableTemplateEngines: false, - IsDevelopment: false, - TimeFormat: DefaultTimeFormat, - Charset: DefaultCharset, - Gzip: false, - Sessions: DefaultSessionsConfiguration(), - Websocket: DefaultWebsocketConfiguration(), - Other: options.Options{}, + VHost: "", + VScheme: "", + ReadTimeout: DefaultReadTimeout, + WriteTimeout: DefaultWriteTimeout, + MaxHeaderBytes: DefaultMaxHeaderBytes, + CheckForUpdates: false, + CheckForUpdatesSync: false, + DisablePathCorrection: DefaultDisablePathCorrection, + EnablePathEscape: DefaultEnablePathEscape, + FireMethodNotAllowed: false, + DisableBanner: false, + DisableBodyConsumptionOnUnmarshal: false, + LoggerOut: DefaultLoggerOut, + LoggerPreffix: DefaultLoggerPreffix, + DisableTemplateEngines: false, + IsDevelopment: false, + TimeFormat: DefaultTimeFormat, + Charset: DefaultCharset, + Gzip: false, + Sessions: DefaultSessionsConfiguration(), + Websocket: DefaultWebsocketConfiguration(), + Other: options.Options{}, } } diff --git a/context.go b/context.go index 061ca402..e7a9c4b3 100644 --- a/context.go +++ b/context.go @@ -548,6 +548,12 @@ func (ctx *Context) UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error return err } + if ctx.framework.Config.DisableBodyConsumptionOnUnmarshal { + // * remember, Request.Body has no Bytes(), we have to consume them first + // and after re-set them to the body, this is the only solution. + ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rawData)) + } + // check if the v contains its own decode // in this case the v should be a pointer also, // but this is up to the user's custom Decode implementation* diff --git a/context_test.go b/context_test.go index 5f3360ed..89170653 100644 --- a/context_test.go +++ b/context_test.go @@ -251,6 +251,13 @@ func testUnmarshaler(t *testing.T, tb *testBinder, if write != nil { write(ctx) } + + if iris.Config.DisableBodyConsumptionOnUnmarshal { + rawData, _ := ioutil.ReadAll(ctx.Request.Body) + if len(rawData) == 0 { + t.Fatalf("Expected data to NOT BE consumed by the previous UnmarshalBody call but we got empty body.") + } + } } iris.Post("/bind_req_body", h) @@ -301,7 +308,6 @@ func TestContextBinders(t *testing.T) { expectedObj.Birth + `` + strconv.Itoa(expectedObj.Stars) + `` - // JSON vXML := &testBinder{&testBinderXMLData{}, iris.UnmarshalerFunc(xml.Unmarshal), false} testUnmarshaler( @@ -315,6 +321,18 @@ func TestContextBinders(t *testing.T) { Status(iris.StatusOK). Body().Equal(expectedAndPassedObjText) + // JSON with DisableBodyConsumptionOnUnmarshal + iris.Config.DisableBodyConsumptionOnUnmarshal = true + testUnmarshaler( + t, + vJSON, + func(ctx *iris.Context) { + ctx.JSON(iris.StatusOK, vJSON.vp) + }). + WithJSON(passed). + Expect(). + Status(iris.StatusOK). + JSON().Object().Equal(expectedObject) } func TestContextReadForm(t *testing.T) { diff --git a/iris.go b/iris.go index 18358603..06f2ba20 100644 --- a/iris.go +++ b/iris.go @@ -81,7 +81,7 @@ const ( // IsLongTermSupport flag is true when the below version number is a long-term-support version IsLongTermSupport = false // Version is the current version number of the Iris web framework - Version = "6.1.2" + Version = "6.1.3" banner = ` _____ _ |_ _| (_)