From 1b1661ed538b4bff80ddaf59742754514c8dea2c Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 31 Dec 2017 02:32:28 +0200 Subject: [PATCH] ok almost finished. We're good at deadlines eventually. Tomorrow at 23:59 an article will be published same time with the dev branch merge to master Former-commit-id: 42c1bf88cedbddf3cc01366ab769139546902e71 --- FAQ.md | 19 ++- HISTORY.md | 160 +++++++++++++++++- README.md | 22 ++- VERSION | 2 +- _examples/README.md | 8 +- _examples/hero/basic/README.md | 4 +- _examples/http_request/upload-file/main.go | 73 ++++++++ .../upload-file/templates/upload_form.html | 12 ++ .../http_request/upload-file/uploads/.gitkeep | 0 _examples/http_request/upload-files/main.go | 62 +++---- .../upload-files/templates/upload_form.html | 2 +- .../upload-files/uploads/.gitkeep | 0 .../quicktemplate/templates/base.qtpl.go | 21 ++- .../quicktemplate/templates/hello.qtpl.go | 10 ++ .../quicktemplate/templates/index.qtpl.go | 10 ++ _examples/mvc/basic/main.go | 34 ++-- configuration.go | 6 +- context/configuration.go | 2 +- context/context.go | 33 +++- core/maintenance/version.go | 2 +- deprecated.go | 1 + doc.go | 4 +- 22 files changed, 399 insertions(+), 88 deletions(-) create mode 100644 _examples/http_request/upload-file/main.go create mode 100644 _examples/http_request/upload-file/templates/upload_form.html create mode 100644 _examples/http_request/upload-file/uploads/.gitkeep create mode 100644 _examples/http_request/upload-files/uploads/.gitkeep diff --git a/FAQ.md b/FAQ.md index 26c0bcf1..1d018dd6 100644 --- a/FAQ.md +++ b/FAQ.md @@ -60,17 +60,30 @@ Iris may have reached version 8, but we're not stopping there. We have many feat Yes, not only because you will learn Golang in the same time, but there are some positions open for Iris-specific developers the time we speak. -- https://glints.id/opportunities/jobs/5553 +Go to our facebook page, like it and receive notifications about new job offers, we already have couple of them stay at the top of the page: https://www.facebook.com/iris.framework + + ## Do we have a community Chat? -Yes, https://kataras.rocket.chat/channel/iris. +Yes, https://chat.iris-go.com https://github.com/kataras/iris/issues/646 @@ -78,4 +91,4 @@ https://github.com/kataras/iris/issues/646 By normal people like you, who help us by donating small or larger amounts of money. -Help this project to continue deliver awesome and unique features with the higher code quality as possible by donating any amount via [PayPal](https://www.paypal.me/kataras)! \ No newline at end of file +Help this project to continue deliver awesome and unique features with the highest possible code quality as possible by donating any amount via [PayPal](https://www.paypal.me/kataras). Your name will be published [here](https://iris-go.com/donate) after your approval via e-mail. \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 7e43f088..2f1f7623 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -16,19 +16,161 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene > Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes. **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you. - +> The new version adds 75 plus new commits, read them if you are developing a web framework based on Iris. Why v10? 9 was skipped for undefined reasons. + +## Hero + +The new package [hero](hero) contains features for binding any object or function that `handlers` may use, these are called dependencies. Hero funcs can also return any type of values, these values will be dispatched to the client. + +> You may saw binding before but you didn't have code editor's support, with Iris you get truly safe binding thanks to the new `hero` package. It's also fast, near to raw handlers performance because Iris calculates everything before server ran! + +Below you will see some screenshots we prepared for you in order to be easier to understand: + +### 1. Path Parameters - Built'n Dependencies + +![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) + +### 2. Services - Static Dependencies + +![](https://github.com/kataras/explore/raw/master/iris/hero/hero-2-monokai.png) + +### 3. Per-Request - Dynamic Dependencies + +![](https://github.com/kataras/explore/raw/master/iris/hero/hero-3-monokai.png) + +`hero funcs` are very easy to understand and when you start using them **you never go back**. + +Examples: + +- [Basic](_examples/hero/basic/main.go) +- [Overview](_examples/hero/overview) + +## MVC + +You have to understand the `hero` package in order to use the `mvc`, because `mvc` uses the `hero` internally for the controller's methods you use as routes, the same rules applied to those controller's methods of yours as well. + +With this version you can register **any controller's methods as routes manually**, you can **get a route based on a method name and change its `Name` (useful for reverse routing inside templates)**, you can use any **dependencies** registered from `hero.Register` or `mvc.New(iris.Party).Register` per mvc application or per-controller, **you can still use `BeginRequest` and `EndRequest`**, you can catch **`BeforeActivation(b mvc.BeforeActivation)` to add dependencies per controller and `AfterActivation(a mvc.AfterActivation)` to make any post-validations**, **singleton controllers when no dynamic dependencies are used**, **Websocket controller, as simple as a `websocket.Connection` dependency** and more... + +Examples: + +**If you used MVC before then read very carefully: MVC CONTAINS SOME BREAKING CHANGES BUT YOU CAN DO A LOT MORE AND EVEN FASTER THAN BEFORE** + +**PLEASE READ THE EXAMPLES CAREFULLY, WE'VE MADE THEM FOR YOU** + +Old examples are here as well. Compare the two different versions of each example to understand what you win if you upgrade now. + +| NEW | OLD | +| -----------|-------------| +| [Hello world](_examples/mvc/hello-world/main.go) | [OLD Hello world](https://github.com/kataras/iris/blob/v8/_examples/mvc/hello-world/main.go) | +| [Session Controller](_examples/mvc/session-controller/main.go) | [OLD Session Controller](https://github.com/kataras/iris/blob/v8/_examples/mvc/session-controller/main.go) | +| [Overview - Plus Repository and Service layers](_examples/mvc/overview) | [OLD Overview - Plus Repository and Service layers](https://github.com/kataras/iris/tree/v8/_examples/mvc/overview) | +| [Login showcase - Plus Repository and Service layers](_examples/mvc/login) | [OLD Login showcase - Plus Repository and Service layers](https://github.com/kataras/iris/tree/v8/_examples/mvc/login) | +| [Singleton](_examples/mvc/singleton) | **NEW** | +| [Websocket Controller](_examples/mvc/websocket) | **NEW** | +| [Vue.js Todo MVC](_examples/tutorial/vuejs-todo-mvc) | **NEW** | + +## context#PostMaxMemory + +Remove the old static variable `context.DefaultMaxMemory` and replace it with the configuration `WithPostMaxMemory`. + +```go +// 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 +``` + +If you used that old static field you will have to change that single line. + +Usage: + +```go +import "github.com/kataras/iris" + +func main() { + app := iris.New() + // [...] + + app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(10 << 20)) +} +``` + +## context#UploadFormFiles + +New method to upload multiple files, should be used for common upload actions, it's just a helper function. + +```go +// UploadFormFiles uploads any received file(s) from the client +// to the system physical location "destDirectory". +// +// The second optional argument "before" gives caller the chance to +// modify the *miltipart.FileHeader before saving to the disk, +// it can be used to change a file's name based on the current request, +// all FileHeader's options can be changed. You can ignore it if +// you don't need to use this capability before saving a file to the disk. +// +// Note that it doesn't check if request body streamed. +// +// Returns the copied length as int64 and +// a not nil error if at least one new file +// can't be created due to the operating system's permissions or +// http.ErrMissingFile if no file received. +// +// If you want to receive & accept files and manage them manually you can use the `context#FormFile` +// instead and create a copy function that suits your needs, the below is for generic usage. +// +// 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. +// +// See `FormFile` to a more controlled to receive a file. +func (ctx *context) UploadFormFiles( + destDirectory string, + before ...func(string, string), + ) (int64, error) +``` + +Example can be found [here](_examples/http_request/upload-files/main.go). + +## context#View + +Just a minor addition, add a second optional variadic argument to the `context#view` method to accept a single value for template binding. +When you just want one value and not key-value pairs, you used to use an empty string on the `ViewData`, which is fine, especially if you preload these from a previous handler/middleware in the request handlers chain. + +```go +func(ctx iris.Context) { + ctx.ViewData("", myItem{Name: "iris" }) + ctx.View("item.html") +} +``` + +Same as: + +```go +func(ctx iris.Context) { + ctx.View("item.html", myItem{Name: "iris" }) +} +``` + +```html +Item's name: {{.Name}} +``` + +## Session#GetString + +`sessions/session#GetString` can now return a filled value even if the stored value is a type of integer, just like the memstore, the context's temp store, the context's path parameters and the context's url parameters. \ No newline at end of file diff --git a/README.md b/README.md index 0c20498a..248abb1b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples) [![release](https://img.shields.io/badge/release%20-v10.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples) [![release](https://img.shields.io/badge/release%20-v10.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris is a fast, simple yet fully featured and very efficient web framework for Go. @@ -41,9 +41,10 @@ _Updated at: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_ ## Support -- [HISTORY](HISTORY.md) file is your best friend, it contains information about all the new features for the current release, you can even search for [older versions](https://github.com/kataras/iris/releases) +- [HISTORY](HISTORY.md#mo-01-jenuary-2018--v1000) file is your best friend, it contains information about the latest features and changes - Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues) - Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com) +- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) - Do you like the framework? Tweet something about it! The People have spoken: @@ -81,15 +82,11 @@ _Updated at: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_ - - - - -For more information about contributing to the Iris project please check the [CONTRIBUTING.md](CONTRIBUTING.md) file. +> For more information about contributing to the Iris project please check the [CONTRIBUTING.md](CONTRIBUTING.md) file. [List of all Contributors](https://github.com/kataras/iris/graphs/contributors) @@ -103,6 +100,7 @@ First of all, the most correct way to begin with a web framework is to learn the ### Iris starter kits + + +1. [A basic web app built in Iris for Go](https://github.com/gauravtiwari/go_iris_app) +2. [A mini social-network created with the awesome Iris๐Ÿ’–๐Ÿ’–](https://github.com/iris-contrib/Iris-Mini-Social-Network) +3. [Iris isomorphic react/hot reloadable/redux/css-modules starter kit](https://github.com/iris-contrib/iris-starter-kit) +4. [Demo project with react using typescript and Iris](https://github.com/ionutvilie/react-ts) +5. [Self-hosted Localization Management Platform built with Iris and Angular](https://github.com/iris-contrib/parrot) +6. [Iris + Docker and Kubernetes](https://github.com/iris-contrib/cloud-native-go) +7. [Quickstart for Iris with Nanobox](https://guides.nanobox.io/golang/iris/from-scratch) +8. [A Hasura starter project with a ready to deploy Golang hello-world web app with IRIS](https://hasura.io/hub/project/hasura/hello-golang-iris) > Did you build something similar? Let us [know](https://github.com/kataras/iris/pulls)! diff --git a/VERSION b/VERSION index 87e1dd41..2823854a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.5.8:https://github.com/kataras/iris/blob/master/HISTORY.md#th-09-november-2017--v858 \ No newline at end of file +10.0.0:https://github.com/kataras/iris/blob/master/HISTORY.md#mo-01-jenuary-2018--v1000 \ No newline at end of file diff --git a/_examples/README.md b/_examples/README.md index 1f69fa4d..647e008d 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -112,6 +112,11 @@ Navigate through examples for a better understanding. * [per-route](routing/writing-a-middleware/per-route/main.go) * [globally](routing/writing-a-middleware/globally/main.go) +### hero + +- [Basic](hero/basic/main.go) +- [Overview](hero/overview) + ### MVC ![](mvc/web_mvc_diagram.png) @@ -313,7 +318,8 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her - [Bind JSON](http_request/read-json/main.go) - [Bind Form](http_request/read-form/main.go) -- [Upload/Read Files](http_request/upload-files/main.go) +- [Upload/Read File](http_request/upload-file/main.go) +- [Upload multiple files with an easy way](http_request/upload-files/main.go) > The `context.Request()` returns the same *http.Request you already know, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris. diff --git a/_examples/hero/basic/README.md b/_examples/hero/basic/README.md index 59465659..c039ce97 100644 --- a/_examples/hero/basic/README.md +++ b/_examples/hero/basic/README.md @@ -8,6 +8,6 @@ ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-2-monokai.png) -## References +## 3. Per-Request - Dynamic Dependencies -- [explore](https://github.com/kataras/explore) \ No newline at end of file +![](https://github.com/kataras/explore/raw/master/iris/hero/hero-3-monokai.png) \ No newline at end of file diff --git a/_examples/http_request/upload-file/main.go b/_examples/http_request/upload-file/main.go new file mode 100644 index 00000000..38af8c09 --- /dev/null +++ b/_examples/http_request/upload-file/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "crypto/md5" + "fmt" + "io" + "os" + "strconv" + "time" + + "github.com/kataras/iris" +) + +func main() { + app := iris.New() + + app.RegisterView(iris.HTML("./templates", ".html")) + + // Serve the upload_form.html to the client. + app.Get("/upload", func(ctx iris.Context) { + // create a token (optionally). + + now := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(now, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + // render the form with the token for any use you'd like. + // ctx.ViewData("", token) + // or add second argument to the `View` method. + // Token will be passed as {{.}} in the template. + ctx.View("upload_form.html", token) + }) + + // Handle the post request from the upload_form.html to the server + app.Post("/upload", func(ctx iris.Context) { + // iris.LimitRequestBodySize(32 <<20) as middleware to a route + // or use ctx.SetMaxRequestBodySize(32 << 20) + // to limit the whole request body size, + // + // or let the configuration option at app.Run for global setting + // for POST/PUT methods, including uploads of course. + + // Get the file from the request. + file, info, err := ctx.FormFile("uploadfile") + + if err != nil { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.HTML("Error while uploading: " + err.Error() + "") + return + } + + defer file.Close() + fname := info.Filename + + // Create a file with the same name + // assuming that you have a folder named 'uploads' + out, err := os.OpenFile("./uploads/"+fname, + os.O_WRONLY|os.O_CREATE, 0666) + + if err != nil { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.HTML("Error while uploading: " + err.Error() + "") + return + } + defer out.Close() + + io.Copy(out, file) + }) + + // start the server at http://localhost:8080 with post limit at 32 MB. + app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(32<<20)) +} diff --git a/_examples/http_request/upload-file/templates/upload_form.html b/_examples/http_request/upload-file/templates/upload_form.html new file mode 100644 index 00000000..9782eadf --- /dev/null +++ b/_examples/http_request/upload-file/templates/upload_form.html @@ -0,0 +1,12 @@ + + +Upload file + + +
+ +
+ + diff --git a/_examples/http_request/upload-file/uploads/.gitkeep b/_examples/http_request/upload-file/uploads/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/_examples/http_request/upload-files/main.go b/_examples/http_request/upload-files/main.go index 8800922a..25058b6e 100644 --- a/_examples/http_request/upload-files/main.go +++ b/_examples/http_request/upload-files/main.go @@ -4,8 +4,9 @@ import ( "crypto/md5" "fmt" "io" - "os" + "mime/multipart" "strconv" + "strings" "time" "github.com/kataras/iris" @@ -26,46 +27,39 @@ func main() { token := fmt.Sprintf("%x", h.Sum(nil)) // render the form with the token for any use you'd like. - ctx.ViewData("", token) - ctx.View("upload_form.html") + ctx.View("upload_form.html", token) }) - // Handle the post request from the upload_form.html to the server + // Handle the post request from the upload_form.html to the server. app.Post("/upload", func(ctx iris.Context) { - // iris.LimitRequestBodySize(32 <<20) as middleware to a route - // or use ctx.SetMaxRequestBodySize(32 << 20) - // to limit the whole request body size, // - // or let the configuration option at app.Run for global setting - // for POST/PUT methods, including uploads of course. + // UploadFormFiles + // uploads any number of incoming files (multiple property on the form input). + // - // Get the file from the request. - file, info, err := ctx.FormFile("uploadfile") - - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.HTML("Error while uploading: " + err.Error() + "") - return - } - - defer file.Close() - fname := info.Filename - - // Create a file with the same name - // assuming that you have a folder named 'uploads' - out, err := os.OpenFile("./uploads/"+fname, - os.O_WRONLY|os.O_CREATE, 0666) - - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.HTML("Error while uploading: " + err.Error() + "") - return - } - defer out.Close() - - io.Copy(out, file) + // second argument is totally optionally, + // it can be used to change a file's name based on the request, + // at this example we will showcase how to use it + // by prefixing the uploaded file with the current user's ip. + ctx.UploadFormFiles("./uploads", beforeSave) }) // start the server at http://localhost:8080 with post limit at 32 MB. app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(32<<20)) } + +func beforeSave(ctx iris.Context, file *multipart.FileHeader) { + ip := ctx.RemoteAddr() + // make sure you format the ip in a way + // that can be used for a file name (simple case): + ip = strings.Replace(ip, ".", "_", -1) + ip = strings.Replace(ip, ":", "_", -1) + + // you can use the time.Now, to prefix or suffix the files + // based on the current time as well, as an exercise. + // i.e unixTime := time.Now().Unix() + // prefix the Filename with the $IP- + // no need for more actions, internal uploader will use this + // name to save the file into the "./uploads" folder. + file.Filename = ip + "-" + file.Filename +} diff --git a/_examples/http_request/upload-files/templates/upload_form.html b/_examples/http_request/upload-files/templates/upload_form.html index 17c4ae9b..33607416 100644 --- a/_examples/http_request/upload-files/templates/upload_form.html +++ b/_examples/http_request/upload-files/templates/upload_form.html @@ -5,7 +5,7 @@
-
diff --git a/_examples/http_request/upload-files/uploads/.gitkeep b/_examples/http_request/upload-files/uploads/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go index 32a5e638..d1e988f1 100644 --- a/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go +++ b/_examples/http_responsewriter/quicktemplate/templates/base.qtpl.go @@ -5,9 +5,11 @@ // //line base.qtpl:3 + package templates //line base.qtpl:3 + import ( qtio422016 "io" @@ -15,12 +17,14 @@ import ( ) //line base.qtpl:3 + var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line base.qtpl:4 + type Partial interface { //line base.qtpl:4 Body() string @@ -29,11 +33,13 @@ type Partial interface { //line base.qtpl:4 WriteBody(qq422016 qtio422016.Writer) //line base.qtpl:4 + } // Template writes a template implementing the Partial interface. //line base.qtpl:11 + func StreamTemplate(qw422016 *qt422016.Writer, p Partial) { //line base.qtpl:11 qw422016.N().S(` @@ -61,9 +67,11 @@ func StreamTemplate(qw422016 *qt422016.Writer, p Partial) { `) //line base.qtpl:30 + } //line base.qtpl:30 + func WriteTemplate(qq422016 qtio422016.Writer, p Partial) { //line base.qtpl:30 qw422016 := qt422016.AcquireWriter(qq422016) @@ -72,9 +80,11 @@ func WriteTemplate(qq422016 qtio422016.Writer, p Partial) { //line base.qtpl:30 qt422016.ReleaseWriter(qw422016) //line base.qtpl:30 + } //line base.qtpl:30 + func Template(p Partial) string { //line base.qtpl:30 qb422016 := qt422016.AcquireByteBuffer() @@ -87,21 +97,27 @@ func Template(p Partial) string { //line base.qtpl:30 return qs422016 //line base.qtpl:30 + } // Base template implementation. Other pages may inherit from it if they need // overriding only certain Partial methods. //line base.qtpl:35 + type Base struct{} //line base.qtpl:36 + func (b *Base) StreamBody(qw422016 *qt422016.Writer) { //line base.qtpl:36 -qw422016.N().S(`This is the base body`) } + + qw422016.N().S(`This is the base body`) +} //line base.qtpl:36 //line base.qtpl:36 + func (b *Base) WriteBody(qq422016 qtio422016.Writer) { //line base.qtpl:36 qw422016 := qt422016.AcquireWriter(qq422016) @@ -110,9 +126,11 @@ func (b *Base) WriteBody(qq422016 qtio422016.Writer) { //line base.qtpl:36 qt422016.ReleaseWriter(qw422016) //line base.qtpl:36 + } //line base.qtpl:36 + func (b *Base) Body() string { //line base.qtpl:36 qb422016 := qt422016.AcquireByteBuffer() @@ -125,4 +143,5 @@ func (b *Base) Body() string { //line base.qtpl:36 return qs422016 //line base.qtpl:36 + } diff --git a/_examples/http_responsewriter/quicktemplate/templates/hello.qtpl.go b/_examples/http_responsewriter/quicktemplate/templates/hello.qtpl.go index 5b812bfb..61db387a 100644 --- a/_examples/http_responsewriter/quicktemplate/templates/hello.qtpl.go +++ b/_examples/http_responsewriter/quicktemplate/templates/hello.qtpl.go @@ -5,9 +5,11 @@ // //line hello.qtpl:3 + package templates //line hello.qtpl:3 + import ( qtio422016 "io" @@ -15,17 +17,20 @@ import ( ) //line hello.qtpl:3 + var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line hello.qtpl:4 + type Hello struct { Vars map[string]interface{} } //line hello.qtpl:9 + func (h *Hello) StreamBody(qw422016 *qt422016.Writer) { //line hello.qtpl:9 qw422016.N().S(` @@ -43,9 +48,11 @@ func (h *Hello) StreamBody(qw422016 *qt422016.Writer) { `) //line hello.qtpl:14 + } //line hello.qtpl:14 + func (h *Hello) WriteBody(qq422016 qtio422016.Writer) { //line hello.qtpl:14 qw422016 := qt422016.AcquireWriter(qq422016) @@ -54,9 +61,11 @@ func (h *Hello) WriteBody(qq422016 qtio422016.Writer) { //line hello.qtpl:14 qt422016.ReleaseWriter(qw422016) //line hello.qtpl:14 + } //line hello.qtpl:14 + func (h *Hello) Body() string { //line hello.qtpl:14 qb422016 := qt422016.AcquireByteBuffer() @@ -69,4 +78,5 @@ func (h *Hello) Body() string { //line hello.qtpl:14 return qs422016 //line hello.qtpl:14 + } diff --git a/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go b/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go index e5297ac4..18b4824d 100644 --- a/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go +++ b/_examples/http_responsewriter/quicktemplate/templates/index.qtpl.go @@ -5,9 +5,11 @@ // //line index.qtpl:3 + package templates //line index.qtpl:3 + import ( qtio422016 "io" @@ -15,15 +17,18 @@ import ( ) //line index.qtpl:3 + var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line index.qtpl:4 + type Index struct{} //line index.qtpl:7 + func (i *Index) StreamBody(qw422016 *qt422016.Writer) { //line index.qtpl:7 qw422016.N().S(` @@ -33,9 +38,11 @@ func (i *Index) StreamBody(qw422016 *qt422016.Writer) { `) //line index.qtpl:12 + } //line index.qtpl:12 + func (i *Index) WriteBody(qq422016 qtio422016.Writer) { //line index.qtpl:12 qw422016 := qt422016.AcquireWriter(qq422016) @@ -44,9 +51,11 @@ func (i *Index) WriteBody(qq422016 qtio422016.Writer) { //line index.qtpl:12 qt422016.ReleaseWriter(qw422016) //line index.qtpl:12 + } //line index.qtpl:12 + func (i *Index) Body() string { //line index.qtpl:12 qb422016 := qt422016.AcquireByteBuffer() @@ -59,4 +68,5 @@ func (i *Index) Body() string { //line index.qtpl:12 return qs422016 //line index.qtpl:12 + } diff --git a/_examples/mvc/basic/main.go b/_examples/mvc/basic/main.go index 1439af63..c6dca3f5 100644 --- a/_examples/mvc/basic/main.go +++ b/_examples/mvc/basic/main.go @@ -12,12 +12,12 @@ import ( func main() { app := iris.New() app.Logger().SetLevel("debug") - mvc.Configure(app.Party("/todo"), TodoApp) + mvc.Configure(app.Party("/basic"), basicMVC) app.Run(iris.Addr(":8080")) } -func TodoApp(app *mvc.Application) { +func basicMVC(app *mvc.Application) { // You can use normal middlewares at MVC apps of course. app.Router.Use(func(ctx iris.Context) { ctx.Application().Logger().Infof("Path: %s", ctx.Path()) @@ -32,16 +32,16 @@ func TodoApp(app *mvc.Application) { &prefixedLogger{prefix: "DEV"}, ) - // GET: http://localhost:8080/todo - // GET: http://localhost:8080/todo/custom - app.Handle(new(TodoController)) + // GET: http://localhost:8080/basic + // GET: http://localhost:8080/basic/custom + app.Handle(new(basicController)) // All dependencies of the parent *mvc.Application // are cloned to this new child, // thefore it has access to the same session as well. - // GET: http://localhost:8080/todo/sub + // GET: http://localhost:8080/basic/sub app.Party("/sub"). - Handle(new(TodoSubController)) + Handle(new(basicSubController)) } // If controller's fields (or even its functions) expecting an interface @@ -64,39 +64,39 @@ func (s *prefixedLogger) Log(msg string) { fmt.Printf("%s: %s\n", s.prefix, msg) } -type TodoController struct { +type basicController struct { Logger LoggerService Session *sessions.Session } -func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) { +func (c *basicController) BeforeActivation(b mvc.BeforeActivation) { b.Handle("GET", "/custom", "Custom") } -func (c *TodoController) AfterActivation(a mvc.AfterActivation) { +func (c *basicController) AfterActivation(a mvc.AfterActivation) { if a.Singleton() { - panic("TodoController should be stateless, a request-scoped, we have a 'Session' which depends on the context.") + panic("basicController should be stateless, a request-scoped, we have a 'Session' which depends on the context.") } } -func (c *TodoController) Get() string { +func (c *basicController) Get() string { count := c.Session.Increment("count", 1) - body := fmt.Sprintf("Hello from TodoController\nTotal visits from you: %d", count) + body := fmt.Sprintf("Hello from basicController\nTotal visits from you: %d", count) c.Logger.Log(body) return body } -func (c *TodoController) Custom() string { +func (c *basicController) Custom() string { return "custom" } -type TodoSubController struct { +type basicSubController struct { Session *sessions.Session } -func (c *TodoSubController) Get() string { +func (c *basicSubController) Get() string { count, _ := c.Session.GetIntDefault("count", 1) - return fmt.Sprintf("Hello from TodoSubController.\nRead-only visits count: %d", count) + return fmt.Sprintf("Hello from basicSubController.\nRead-only visits count: %d", count) } diff --git a/configuration.go b/configuration.go index 55cf7658..02526963 100644 --- a/configuration.go +++ b/configuration.go @@ -318,7 +318,7 @@ 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. +// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`. // // Defaults to 32MB or 32 << 20 if you prefer. func WithPostMaxMemory(limit int64) Configurator { @@ -478,7 +478,7 @@ type Configuration struct { // 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. + // by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`. // // Defaults to 32MB or 32 << 20 if you prefer. PostMaxMemory int64 `json:"postMaxMemory" yaml:"PostMaxMemory" toml:"PostMaxMemory"` @@ -601,7 +601,7 @@ func (c Configuration) 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. +// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`. // // Defaults to 32MB or 32 << 20 if you prefer. func (c Configuration) GetPostMaxMemory() int64 { diff --git a/context/configuration.go b/context/configuration.go index ad8a7fef..20ff827f 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -57,7 +57,7 @@ type ConfigurationReadOnly interface { // 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. + // by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`. // // Defaults to 32MB or 32 << 20 if you prefer. GetPostMaxMemory() int64 diff --git a/context/context.go b/context/context.go index 64a7abaf..19a38df4 100644 --- a/context/context.go +++ b/context/context.go @@ -551,7 +551,13 @@ type Context interface { FormFile(key string) (multipart.File, *multipart.FileHeader, error) // UploadFormFiles uploads any received file(s) from the client // to the system physical location "destDirectory". - // The root directory must already exists. + // + // The second optional argument "before" gives caller the chance to + // modify the *miltipart.FileHeader before saving to the disk, + // it can be used to change a file's name based on the current request, + // all FileHeader's options can be changed. You can ignore it if + // you don't need to use this capability before saving a file to the disk. + // // Note that it doesn't check if request body streamed. // // Returns the copied length as int64 and @@ -564,7 +570,10 @@ 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. - UploadFormFiles(destDirectory string) (int64, error) + // + // See `FormFile` to a more controlled to receive a file. + UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) + // +------------------------------------------------------------+ // | Custom HTTP Errors | // +------------------------------------------------------------+ @@ -1780,7 +1789,12 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, // UploadFormFiles uploads any received file(s) from the client // to the system physical location "destDirectory". -// The root directory must already exists. +// +// The second optional argument "before" gives caller the chance to +// modify the *miltipart.FileHeader before saving to the disk, +// it can be used to change a file's name based on the current request, +// all FileHeader's options can be changed. You can ignore it if +// you don't need to use this capability before saving a file to the disk. // // Note that it doesn't check if request body streamed. // @@ -1794,7 +1808,9 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, // // 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. -func (ctx *context) UploadFormFiles(destDirectory string) (n int64, err error) { +// +// See `FormFile` to a more controlled to receive a file. +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 { return 0, err @@ -1804,6 +1820,11 @@ func (ctx *context) UploadFormFiles(destDirectory string) (n int64, err error) { if fhs := ctx.request.MultipartForm.File; fhs != nil { for _, files := range fhs { for _, file := range files { + + for _, b := range before { + b(ctx, file) + } + n0, err0 := uploadTo(file, destDirectory) if err0 != nil { return 0, err0 @@ -1824,7 +1845,9 @@ func uploadTo(fh *multipart.FileHeader, destDirectory string) (int64, error) { } defer src.Close() - out, err := os.Create(filepath.Join(destDirectory, fh.Filename)) + out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename), + os.O_WRONLY|os.O_CREATE, os.FileMode(0666)) + if err != nil { return 0, err } diff --git a/core/maintenance/version.go b/core/maintenance/version.go index 2523b3d8..ffed1c9c 100644 --- a/core/maintenance/version.go +++ b/core/maintenance/version.go @@ -13,7 +13,7 @@ import ( const ( // Version is the string representation of the current local Iris Web Framework version. - Version = "8.5.8" + Version = "10.0.0" ) // CheckForUpdates checks for any available updates diff --git a/deprecated.go b/deprecated.go index 192810ef..a5b7a821 100644 --- a/deprecated.go +++ b/deprecated.go @@ -12,6 +12,7 @@ import ( // https://github.com/kataras/iris/blob/master/HISTORY.md#mo-01-jenuary-2018--v10 func (app *Application) Controller(relPath string, c interface{}, _ ...interface{}) []*router.Route { name := mvc.NameOf(c) + panic(fmt.Errorf(`"Controller" method is DEPRECATED, use the "mvc" subpackage instead. PREVIOUSLY YOU USED TO CODE IT LIKE THIS: diff --git a/doc.go b/doc.go index 1bff4e23..4ae3be38 100644 --- a/doc.go +++ b/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Iris Authors. All rights reserved. +// Copyright (c) 2017-2018 The Iris Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub: Current Version -8.5.8 +10.0.0 Installation