simplify readme

Former-commit-id: 95158c51fc01195b60f9ee873ab416c314568198
This commit is contained in:
Gerasimos (Makis) Maropoulos 2018-08-04 13:08:18 +03:00
parent 8b5c106d1c
commit 1ec8452208

248
README.md
View File

@ -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: <b>" + err.Error() + "</b>")
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: <b>" + err.Error() + "</b>")
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: <b>" + err.Error() + "</b>")
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`.