diff --git a/README.md b/README.md
index 85b98927..60de9f3d 100644
--- a/README.md
+++ b/README.md
@@ -211,128 +211,15 @@ id: 1234; page: 1; name: manu; message: this_is_great
### Upload files
-#### Single file
-
-Detail [example code](_examples/http_request/upload-file/main.go).
+- [single file upload](_examples/http_request/upload-file/main.go)
+- [multi file upload](_examples/http_request/upload-files)
```go
const maxSize = 5 << 20 // 5MB
-func main() {
- app.Post("/upload", func(ctx iris.Context) {
- // Get the file from the request.
- file, info, err := ctx.FormFile("file")
- 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 5 MB
- // (defaults to 32MB, read below).
- app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(maxSize))
-}
-```
-
-```bash
-curl -X POST http://localhost:8080/upload \
- -F "file=@./myfile.zip" \
- -H "Content-Type: multipart/form-data"
-```
-
-* The default post max size is 32MB,
-you can extend it to read more data using the `iris.WithPostMaxMemory(maxSize)` configurator at `app.Run`,
-note that this will not be enough for your needs, read below.
-
-* The faster way to check the size is using the `ctx.GetContentLength()` which returns the whole request's size
-(plus a logical number like 2MB or even 10MB for the rest of the size like headers). You can create a
-middleware to adapt this to any necessary handler.
-
-```go
-myLimiter := func(ctx iris.Context) {
- if ctx.GetContentLength() > maxSize { // + 2 << 20 {
- ctx.StatusCode(iris.StatusRequestEntityTooLarge)
- return
- }
- ctx.Next()
-}
-
-app.Post("/upload", myLimiter, myUploadHandler)
-```
-
-* Most clients will set the "Content-Length" header (like browsers) but it's always better to make sure that any client
-can't send data that your server can't or doesn't want to handle. This can be happen using
-the `app.Use(LimitRequestBodySize(maxSize))` (as app or route middleware)
-or the `ctx.SetMaxRequestBodySize(maxSize)` to limit the request based on a customized logic inside a particular handler, they're the same,
-read below.
-
-* You can force-limit the request body size inside a handler using the `ctx.SetMaxRequestBodySize(maxSize)`,
-this will force the connection to close if the incoming data are larger (most clients will receive it as "connection reset"),
-use that to make sure that the client will not send data that your server can't or doesn't want to accept, as a fallback.
-
-```go
-app.Post("/upload", iris.LimitRequestBodySize(maxSize), myUploadHandler)
-```
-
-OR
-
-```go
-app.Post("/upload", func(ctx iris.Context){
- ctx.SetMaxRequestBodySize(maxSize)
-
- // [...]
-})
-```
-
-* Another way is to receive the data and check the second return value's `Size` value of the `ctx.FormFile`, i.e `info.Size`, this will give you
-the exact file size, not the whole incoming request data length.
-
-```go
-app.Post("/upload", func(ctx iris.Context){
- file, info, err := ctx.FormFile("file")
- if err != nil {
- ctx.StatusCode(iris.StatusInternalServerError)
- ctx.HTML("Error while uploading: " + err.Error() + "")
- return
- }
-
- defer file.Close()
-
- if info.Size > maxSize {
- ctx.StatusCode(iris.StatusRequestEntityTooLarge)
- return
- }
-
- // [...]
-})
-```
-
-#### Multiple files (easy way)
-
-See the detail [example code](_examples/http_request/upload-files).
-
-```go
func main() {
app := iris.Default()
- app.Post("/upload", func(ctx iris.Context) {
+ app.Post("/upload", iris.LimitRequestBodySize(maxSize), func(ctx iris.Context) {
//
// UploadFormFiles
// uploads any number of incoming files ("multiple" property on the form input).
@@ -365,56 +252,6 @@ func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
}
```
-#### Multiple files (manual way)
-
-```go
-app.Post("/upload_manual", func(ctx iris.Context) {
- r := ctx.Request()
- // Get the max post value size passed via iris.WithPostMaxMemory.
- maxSize := ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()
-
- err := r.ParseMultipartForm(maxSize)
- if err != nil {
- ctx.StatusCode(iris.StatusInternalServerError)
- ctx.WriteString(err.Error())
- return
- }
-
- form := r.MultipartForm
-
- files := form.File["files[]"]
- failures := 0
- for _, file := range files {
- _, err = saveUploadedFile(file, "./uploads")
- if err != nil {
- failures++
- ctx.Writef("failed to upload: %s\n", file.Filename)
- }
- }
- ctx.Writef("%d files uploaded", len(files)-failures)
-})
-```
-
-```go
-func saveUploadedFile(fh *multipart.FileHeader, destDirectory string) (int64, error) {
- src, err := fh.Open()
- if err != nil {
- return 0, err
- }
- defer src.Close()
-
- out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename),
- os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
-
- if err != nil {
- return 0, err
- }
- defer out.Close()
-
- return io.Copy(out, src)
-}
-```
-
How to `curl`:
```bash
@@ -585,6 +422,8 @@ func main() {
Iris uses [**go-playground/validator.v9**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v9#hdr-Baked_In_Validators_and_Tags).
+Example [detail code](_examples/http_request/read-json-struct-validation/main.go).
+
Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.
```go
@@ -660,79 +499,48 @@ func main() {
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
-
- // Or collect these as json objects
- // and send back to the client the collected errors via ctx.JSON
- // {
- // "namespace": err.Namespace(),
- // "field": err.Field(),
- // "struct_namespace": err.StructNamespace(),
- // "struct_field": err.StructField(),
- // "tag": err.Tag(),
- // "actual_tag": err.ActualTag(),
- // "kind": err.Kind().String(),
- // "type": err.Type().String(),
- // "value": fmt.Sprintf("%v", err.Value()),
- // "param": err.Param(),
- // }
}
- // from here you can create your own error messages in whatever language you wish.
return
}
// save user to database.
})
- // use Postman or whatever to do a POST request
- // to the http://localhost:8080/user with RAW BODY:
- /*
- {
- "fname": "",
- "lname": "",
- "age": 45,
- "email": "mail@example.com",
- "favColor": "#000",
- "addresses": [{
- "street": "Eavesdown Docks",
- "planet": "Persphone",
- "phone": "none",
- "city": "Unknown"
- }]
- }
- */
- // Content-Type to application/json (optionally but good practise).
- // This request will fail due to the empty `User.FirstName` (fname in json)
- // and `User.LastName` (lname in json).
- // Check your iris' application terminal output.
- app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
+ app.Run(iris.Addr(":8080"))
}
-// UserStructLevelValidation contains custom struct level validations that don't always
-// make sense at the field validation level. For Example this function validates that either
-// FirstName or LastName exist; could have done that with a custom field validation but then
-// would have had to add it to both fields duplicating the logic + overhead, this way it's
-// only validated once.
-//
-// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
-// hooks right into validator and you can combine with validation tags and still have a
-// common error output format.
func UserStructLevelValidation(sl validator.StructLevel) {
-
user := sl.Current().Interface().(User)
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "")
sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "")
}
-
- // plus can to more, even with different tag than "fnameorlname".
}
```
-## Testing
+```json
+{
+ "fname": "",
+ "lname": "",
+ "age": 45,
+ "email": "mail@example.com",
+ "favColor": "#000",
+ "addresses": [{
+ "street": "Eavesdown Docks",
+ "planet": "Persphone",
+ "phone": "none",
+ "city": "Unknown"
+ }]
+}
+```
-First, let's write a simple application which will make use of the HTTP Cookies.
+### Cookies
+
+> Are you looking about [http sessions instead?](_examples/sessions)
+
+Let's write a simple application which will make use of the HTTP Cookies.
```sh
$ cat _examples/cookies/basic/main.go
@@ -795,9 +603,7 @@ func main() {
* `ctx.Request().Cookie(name)` is also available, it's the `net/http` approach
* Learn more about path parameter's types by clicking [here](_examples/routing/dynamic-path/main.go#L31).
-### httptest
-
-Next, is the critical part of this section, the **HTTP Testing**.
+### Testing
Iris offers an incredible support for the [httpexpect](github.com/iris-contrib/httpexpect), a Testing Framework for web applications. However, you are able to use the standard Go's `net/http/httptest` package as well but in this example we will use the `kataras/iris/httptest`.