diff --git a/configuration.go b/configuration.go index 885555f2..5b982e8f 100644 --- a/configuration.go +++ b/configuration.go @@ -315,6 +315,18 @@ func WithCharset(charset string) Configurator { } } +// WithPostMaxMemory sets the maximum post data size +// that a client can send to the server, this differs +// from the overral request body size which can be modified +// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize. +// +// Defaults to 32MB or 32 << 20 if you prefer. +func WithPostMaxMemory(limit int64) Configurator { + return func(app *Application) { + app.config.PostMaxMemory = limit + } +} + // WithRemoteAddrHeader enables or adds a new or existing request header name // that can be used to validate the client's real IP. // @@ -463,6 +475,13 @@ type Configuration struct { // Defaults to "UTF-8". Charset string `json:"charset,omitempty" yaml:"Charset" toml:"Charset"` + // PostMaxMemory sets the maximum post data size + // that a client can send to the server, this differs + // from the overral request body size which can be modified + // by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize. + // + // Defaults to 32MB or 32 << 20 if you prefer. + PostMaxMemory int64 `json:"postMaxMemory" yaml:"PostMaxMemory" toml:"PostMaxMemory"` // +----------------------------------------------------+ // | Context's keys for values used on various featuers | // +----------------------------------------------------+ @@ -579,6 +598,16 @@ func (c Configuration) GetCharset() string { return c.Charset } +// GetPostMaxMemory returns the maximum configured post data size +// that a client can send to the server, this differs +// from the overral request body size which can be modified +// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize. +// +// Defaults to 32MB or 32 << 20 if you prefer. +func (c Configuration) GetPostMaxMemory() int64 { + return c.PostMaxMemory +} + // GetTranslateFunctionContextKey returns the configuration's TranslateFunctionContextKey value, // used for i18n. func (c Configuration) GetTranslateFunctionContextKey() string { @@ -684,6 +713,10 @@ func WithConfiguration(c Configuration) Configurator { main.Charset = v } + if v := c.PostMaxMemory; v > 0 { + main.PostMaxMemory = v + } + if v := c.TranslateFunctionContextKey; v != "" { main.TranslateFunctionContextKey = v } @@ -733,10 +766,17 @@ func DefaultConfiguration() Configuration { DisableAutoFireStatusCode: false, TimeFormat: "Mon, Jan 02 2006 15:04:05 GMT", Charset: "UTF-8", - TranslateFunctionContextKey: "iris.translate", - TranslateLanguageContextKey: "iris.language", - ViewLayoutContextKey: "iris.viewLayout", - ViewDataContextKey: "iris.viewData", + + // PostMaxMemory is for post body max memory. + // + // The request body the size limit + // can be set by the middleware `LimitRequestBodySize` + // or `context#SetMaxRequestBodySize`. + PostMaxMemory: 32 << 20, // 32MB + TranslateFunctionContextKey: "iris.translate", + TranslateLanguageContextKey: "iris.language", + ViewLayoutContextKey: "iris.viewLayout", + ViewDataContextKey: "iris.viewData", RemoteAddrHeaders: map[string]bool{ "X-Real-Ip": false, "X-Forwarded-For": false, diff --git a/context/configuration.go b/context/configuration.go index 4ad53ef8..ad8a7fef 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -54,6 +54,14 @@ type ConfigurationReadOnly interface { // used for templates and the rest of the responses. GetCharset() string + // GetPostMaxMemory returns the maximum configured post data size + // that a client can send to the server, this differs + // from the overral request body size which can be modified + // by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize. + // + // Defaults to 32MB or 32 << 20 if you prefer. + GetPostMaxMemory() int64 + // GetTranslateLanguageContextKey returns the configuration's TranslateFunctionContextKey value, // used for i18n. GetTranslateFunctionContextKey() string diff --git a/context/context.go b/context/context.go index 7616213c..07beb75d 100644 --- a/context/context.go +++ b/context/context.go @@ -486,7 +486,7 @@ type Context interface { // FormValues returns the parsed form data, including both the URL // field's query parameters and the POST or PUT form data. // - // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. + // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. // // NOTE: A check for nil is necessary. FormValues() map[string][]string @@ -540,11 +540,11 @@ type Context interface { // PostValues returns all the parsed form data from POST, PATCH, // or PUT body parameters based on a "name" as a string slice. // - // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. + // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. PostValues(name string) []string // FormFile returns the first uploaded file that received from the client. // - // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. + // The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. FormFile(key string) (multipart.File, *multipart.FileHeader, error) // +------------------------------------------------------------+ @@ -668,7 +668,6 @@ type Context interface { // // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ ViewLayout(layoutTmplFile string) - // ViewData saves one or more key-value pair in order to be passed if and when .View // is being called afterwards, in the same request. // Useful when need to set or/and change template data from previous hanadlers in the chain. @@ -688,7 +687,6 @@ type Context interface { // // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ ViewData(key string, value interface{}) - // GetViewData returns the values registered by `context#ViewData`. // The return value is `map[string]interface{}`, this means that // if a custom struct registered to ViewData then this function @@ -699,16 +697,20 @@ type Context interface { // Similarly to `viewData := ctx.Values().Get("iris.viewData")` or // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`. GetViewData() map[string]interface{} - - // View renders templates based on the adapted view engines. - // First argument accepts the filename, relative to the view engine's Directory, + // View renders a template based on the registered view engine(s). + // First argument accepts the filename, relative to the view engine's Directory and Extension, // i.e: if directory is "./templates" and want to render the "./templates/users/index.html" // then you pass the "users/index.html" as the filename argument. // - // Look: .ViewData and .ViewLayout too. + // The second optional argument can receive a single "view model" + // that will be binded to the view template if it's not nil, + // otherwise it will check for previous view data stored by the `ViewData` + // even if stored at any previous handler(middleware) for the same request. // - // Examples: https://github.com/kataras/iris/tree/master/_examples/view/ - View(filename string) error + // Look .ViewData` and .ViewLayout too. + // + // Examples: https://github.com/kataras/iris/tree/master/_examples/view + View(filename string, optionalViewModel ...interface{}) error // Binary writes out the raw bytes as binary data. Binary(data []byte) (int, error) @@ -1600,7 +1602,7 @@ func (ctx *context) FormValue(name string) string { // FormValues returns the parsed form data, including both the URL // field's query parameters and the POST or PUT form data. // -// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. +// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. // // NOTE: A check for nil is necessary. func (ctx *context) FormValues() map[string][]string { @@ -1624,7 +1626,7 @@ func (ctx *context) form() (form map[string][]string, found bool) { // therefore we don't need to call it here, although it doesn't hurt. // After one call to ParseMultipartForm or ParseForm, // subsequent calls have no effect, are idempotent. - ctx.request.ParseMultipartForm(DefaultMaxMemory) + ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()) if form := ctx.request.Form; len(form) > 0 { return form, true @@ -1733,19 +1735,10 @@ func (ctx *context) PostValueBool(name string) (bool, error) { return strconv.ParseBool(ctx.PostValue(name)) } -const ( - // DefaultMaxMemory is the default value - // for body max memory, defaults to - // 32MB. - // Can be also changed by the middleware `LimitRequestBodySize` - // or `context#SetMaxRequestBodySize`. - DefaultMaxMemory = 32 << 20 // 32 MB -) - // PostValues returns all the parsed form data from POST, PATCH, // or PUT body parameters based on a "name" as a string slice. // -// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. +// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. func (ctx *context) PostValues(name string) []string { ctx.form() return ctx.request.PostForm[name] @@ -1753,11 +1746,41 @@ func (ctx *context) PostValues(name string) []string { // FormFile returns the first uploaded file that received from the client. // -// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultMaxMemory`. +// The default form's memory maximum size is 32MB, it can be changed by the `context#DefaultPostMaxMemory`. func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { return ctx.request.FormFile(key) } +// Copiable is the interface which should be completed +// by files or not that intend to be used inside the `context#CopyFile`. +// This interface allows testing file uploads to your http test as well. +// +// See `CopyFile` for more. +type Copiable interface { + Open() (io.ReadCloser, error) +} + +// CopyFile copies a `context#Copiable` "file", that can be acquired by a `context.FormFile` +// as well, to the "dest". +// +// Returns the copied length as int64 and +// an error if file is not exist, or new file can't be created or closed at the end. +func CopyFile(file Copiable, dest string) (int64, error) { + src, err := file.Open() + if err != nil { + return 0, err + } + defer src.Close() + + out, err := os.Create(dest) + if err != nil { + return 0, err + } + defer out.Close() + + return io.Copy(out, src) +} + // Redirect sends a redirect response to the client // to a specific url or relative path. // accepts 2 parameters string and an optional int @@ -2163,20 +2186,32 @@ func (ctx *context) GetViewData() map[string]interface{} { return nil } -// View renders templates based on the adapted view engines. -// First argument accepts the filename, relative to the view engine's Directory, +// View renders a template based on the registered view engine(s). +// First argument accepts the filename, relative to the view engine's Directory and Extension, // i.e: if directory is "./templates" and want to render the "./templates/users/index.html" // then you pass the "users/index.html" as the filename argument. // -// Look: .ViewData and .ViewLayout too. +// The second optional argument can receive a single "view model" +// that will be binded to the view template if it's not nil, +// otherwise it will check for previous view data stored by the `ViewData` +// even if stored at any previous handler(middleware) for the same request. // -// Examples: https://github.com/kataras/iris/tree/master/_examples/view/ -func (ctx *context) View(filename string) error { +// Look .ViewData and .ViewLayout too. +// +// Examples: https://github.com/kataras/iris/tree/master/_examples/view +func (ctx *context) View(filename string, optionalViewModel ...interface{}) error { ctx.ContentType(ContentHTMLHeaderValue) cfg := ctx.Application().ConfigurationReadOnly() layout := ctx.values.GetString(cfg.GetViewLayoutContextKey()) - bindingData := ctx.values.Get(cfg.GetViewDataContextKey()) + + var bindingData interface{} + if len(optionalViewModel) > 0 { + // a nil can override the existing data or model sent by `ViewData`. + bindingData = optionalViewModel[0] + } else { + bindingData = ctx.values.Get(cfg.GetViewDataContextKey()) + } err := ctx.Application().View(ctx.writer, filename, layout, bindingData) if err != nil { diff --git a/doc.go b/doc.go index 65097759..1bff4e23 100644 --- a/doc.go +++ b/doc.go @@ -1569,7 +1569,7 @@ for this controller, uncomment these if you want: func (c *ExampleController) Options() {} func (c *ExampleController) Trace() {} */ - +// /* func (c *ExampleController) All() {} // OR diff --git a/iris.go b/iris.go index 7b4149ae..a691b5d9 100644 --- a/iris.go +++ b/iris.go @@ -311,6 +311,12 @@ var ( // // Examples: https://github.com/kataras/iris/tree/master/_examples/file-server StaticEmbeddedHandler = router.StaticEmbeddedHandler + // CopyFile copies a `context#Copiable` "file", that can be acquired by a `context.FormFile` + // as well, to the "dest". + // + // Returns the copied length as int64 and + // an error if file is not exist, or new file can't be created or closed at the end. + CopyFile = context.CopyFile // Gzip is a middleware which enables writing // using gzip compression, if client supports. //