mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
minor improvements
This commit is contained in:
parent
5700690dea
commit
8d99b46801
|
@ -599,6 +599,7 @@ New Package-level Variables:
|
||||||
|
|
||||||
New Context Methods:
|
New Context Methods:
|
||||||
|
|
||||||
|
- `Context.SaveFormFile(fh *multipart.FileHeader, dest string) (int64, error)` previously unexported. Accepts a result file of `Context.FormFile` and saves it to the disk.
|
||||||
- `Context.URLParamSlice(name string) []string` is a a shortcut of `ctx.Request().URL.Query()[name]`. Like `URLParam` but it returns all values as a string slice instead of a single string separated by commas.
|
- `Context.URLParamSlice(name string) []string` is a a shortcut of `ctx.Request().URL.Query()[name]`. Like `URLParam` but it returns all values as a string slice instead of a single string separated by commas.
|
||||||
- `Context.PostValueMany(name string) (string, error)` returns the post data of a given key. The returned value is a single string separated by commas on multiple values. It also reports whether the form was empty or when the "name" does not exist or whether the available values are empty. It strips any empty key-values from the slice before return. See `ErrEmptyForm`, `ErrNotFound` and `ErrEmptyFormField` respectfully. The `PostValueInt`, `PostValueInt64`, `PostValueFloat64` and `PostValueBool` now respect the above errors too (the `PostValues` method now returns a second output argument of `error` too, see breaking changes below).
|
- `Context.PostValueMany(name string) (string, error)` returns the post data of a given key. The returned value is a single string separated by commas on multiple values. It also reports whether the form was empty or when the "name" does not exist or whether the available values are empty. It strips any empty key-values from the slice before return. See `ErrEmptyForm`, `ErrNotFound` and `ErrEmptyFormField` respectfully. The `PostValueInt`, `PostValueInt64`, `PostValueFloat64` and `PostValueBool` now respect the above errors too (the `PostValues` method now returns a second output argument of `error` too, see breaking changes below).
|
||||||
- `Context.URLParamsSorted() []memstore.StringEntry` returns a sorted (by key) slice of key-value entries of the URL Query parameters.
|
- `Context.URLParamsSorted() []memstore.StringEntry` returns a sorted (by key) slice of key-value entries of the URL Query parameters.
|
||||||
|
@ -637,6 +638,7 @@ New Context Methods:
|
||||||
|
|
||||||
Breaking Changes:
|
Breaking Changes:
|
||||||
|
|
||||||
|
- `ContextUploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error)` now returns the total files uploaded too (as its first parameter) and the "before" variadic option should return a boolean, if false then the specific file is skipped.
|
||||||
- `Context.PostValues(name string) ([]string, error)` now returns a second output argument of `error` type too, which reports `ErrEmptyForm` or `ErrNotFound` or `ErrEmptyFormField`. The single post value getters now returns the **last value** if multiple was given instead of the first one (this allows clients to append values on flow updates).
|
- `Context.PostValues(name string) ([]string, error)` now returns a second output argument of `error` type too, which reports `ErrEmptyForm` or `ErrNotFound` or `ErrEmptyFormField`. The single post value getters now returns the **last value** if multiple was given instead of the first one (this allows clients to append values on flow updates).
|
||||||
- `Party.GetReporter()` **removed**. The `Application.Build` returns the first error now and the API's errors are logged, this allows the server to run even if some of the routes are invalid but not fatal to the entire application (it was a request from a company).
|
- `Party.GetReporter()` **removed**. The `Application.Build` returns the first error now and the API's errors are logged, this allows the server to run even if some of the routes are invalid but not fatal to the entire application (it was a request from a company).
|
||||||
- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`.
|
- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`.
|
||||||
|
|
|
@ -102,7 +102,7 @@ func uploadView(ctx iris.Context) {
|
||||||
func upload(ctx iris.Context) {
|
func upload(ctx iris.Context) {
|
||||||
ctx.SetMaxRequestBodySize(maxSize)
|
ctx.SetMaxRequestBodySize(maxSize)
|
||||||
|
|
||||||
_, err := ctx.UploadFormFiles(uploadDir, beforeSave)
|
_, _, err := ctx.UploadFormFiles(uploadDir, beforeSave)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.StopWithError(iris.StatusPayloadTooRage, err)
|
ctx.StopWithError(iris.StatusPayloadTooRage, err)
|
||||||
return
|
return
|
||||||
|
@ -111,12 +111,13 @@ func upload(ctx iris.Context) {
|
||||||
ctx.Redirect("/files")
|
ctx.Redirect("/files")
|
||||||
}
|
}
|
||||||
|
|
||||||
func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
|
func beforeSave(ctx iris.Context, file *multipart.FileHeader) bool {
|
||||||
ip := ctx.RemoteAddr()
|
ip := ctx.RemoteAddr()
|
||||||
ip = strings.ReplaceAll(ip, ".", "_")
|
ip = strings.ReplaceAll(ip, ".", "_")
|
||||||
ip = strings.ReplaceAll(ip, ":", "_")
|
ip = strings.ReplaceAll(ip, ":", "_")
|
||||||
|
|
||||||
file.Filename = ip + "-" + file.Filename
|
file.Filename = ip + "-" + file.Filename
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFile(ctx iris.Context) {
|
func deleteFile(ctx iris.Context) {
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -66,7 +64,7 @@ func newApp() *iris.Application {
|
||||||
files := form.File["files[]"]
|
files := form.File["files[]"]
|
||||||
failures := 0
|
failures := 0
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
_, err = saveUploadedFile(file, "./uploads")
|
_, err = ctx.SaveFormFile(file, "./uploads/"+file.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failures++
|
failures++
|
||||||
ctx.Writef("failed to upload: %s\n", file.Filename)
|
ctx.Writef("failed to upload: %s\n", file.Filename)
|
||||||
|
@ -78,24 +76,7 @@ func newApp() *iris.Application {
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveUploadedFile(fh *multipart.FileHeader, destDirectory string) (int64, error) {
|
func beforeSave(ctx iris.Context, file *multipart.FileHeader) bool {
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
|
|
||||||
ip := ctx.RemoteAddr()
|
ip := ctx.RemoteAddr()
|
||||||
// make sure you format the ip in a way
|
// make sure you format the ip in a way
|
||||||
// that can be used for a file name (simple case):
|
// that can be used for a file name (simple case):
|
||||||
|
@ -109,8 +90,9 @@ func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
|
||||||
// no need for more actions, internal uploader will use this
|
// no need for more actions, internal uploader will use this
|
||||||
// name to save the file into the "./uploads" folder.
|
// name to save the file into the "./uploads" folder.
|
||||||
if ip == "" {
|
if ip == "" {
|
||||||
return
|
return true // don't change the file but continue saving it.
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Filename = ip + "-" + file.Filename
|
file.Filename = ip + "-" + file.Filename
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ func readBody(ctx iris.Context) {
|
||||||
var p payload
|
var p payload
|
||||||
|
|
||||||
// Bind request body to "p" depending on the content-type that client sends the data,
|
// Bind request body to "p" depending on the content-type that client sends the data,
|
||||||
// e.g. JSON, XML, YAML, MessagePack, Form, URL Query.
|
// e.g. JSON, XML, YAML, MessagePack, Protobuf, Form and URL Query.
|
||||||
err := ctx.ReadBody(&p)
|
err := ctx.ReadBody(&p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.StopWithProblem(iris.StatusBadRequest,
|
ctx.StopWithProblem(iris.StatusBadRequest,
|
||||||
|
|
|
@ -1925,7 +1925,7 @@ func (ctx *Context) FormFile(key string) (multipart.File, *multipart.FileHeader,
|
||||||
// to the system physical location "destDirectory".
|
// to the system physical location "destDirectory".
|
||||||
//
|
//
|
||||||
// The second optional argument "before" gives caller the chance to
|
// The second optional argument "before" gives caller the chance to
|
||||||
// modify the *miltipart.FileHeader before saving to the disk,
|
// modify or cancel the *miltipart.FileHeader before saving to the disk,
|
||||||
// it can be used to change a file's name based on the current request,
|
// 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
|
// 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.
|
// you don't need to use this capability before saving a file to the disk.
|
||||||
|
@ -1938,52 +1938,60 @@ func (ctx *Context) FormFile(key string) (multipart.File, *multipart.FileHeader,
|
||||||
// http.ErrMissingFile if no file received.
|
// http.ErrMissingFile if no file received.
|
||||||
//
|
//
|
||||||
// If you want to receive & accept files and manage them manually you can use the `context#FormFile`
|
// 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.
|
// instead and create a copy function that suits your needs or use the `SaveFormFile` method,
|
||||||
|
// the below is for generic usage.
|
||||||
//
|
//
|
||||||
// The default form's memory maximum size is 32MB, it can be changed by the
|
// The default form's memory maximum size is 32MB, it can be changed by
|
||||||
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
|
// the `WithPostMaxMemory` configurator or by `SetMaxRequestBodySize` or
|
||||||
|
// by the `LimitRequestBodySize` middleware (depends the use case).
|
||||||
//
|
//
|
||||||
// See `FormFile` to a more controlled to receive a file.
|
// See `FormFile` to a more controlled way to receive a file.
|
||||||
//
|
//
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
|
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
|
||||||
func (ctx *Context) UploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader)) (n int64, err error) {
|
func (ctx *Context) UploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error) {
|
||||||
err = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())
|
err = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.request.MultipartForm != nil {
|
if ctx.request.MultipartForm != nil {
|
||||||
if fhs := ctx.request.MultipartForm.File; fhs != nil {
|
if fhs := ctx.request.MultipartForm.File; fhs != nil {
|
||||||
for _, files := range fhs {
|
for _, files := range fhs {
|
||||||
|
innerLoop:
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
|
||||||
for _, b := range before {
|
for _, b := range before {
|
||||||
b(ctx, file)
|
if !b(ctx, file) {
|
||||||
|
continue innerLoop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n0, err0 := uploadTo(file, destDirectory)
|
n0, err0 := ctx.SaveFormFile(file, filepath.Join(destDirectory, file.Filename))
|
||||||
if err0 != nil {
|
if err0 != nil {
|
||||||
return 0, err0
|
return nil, 0, err0
|
||||||
}
|
}
|
||||||
n += n0
|
n += n0
|
||||||
|
|
||||||
|
uploaded = append(uploaded, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n, nil
|
return uploaded, n, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, http.ErrMissingFile
|
return nil, 0, http.ErrMissingFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func uploadTo(fh *multipart.FileHeader, destDirectory string) (int64, error) {
|
// SaveFormFile saves a result of `FormFile` to the "dest" disk full path (directory + filename).
|
||||||
|
// See `FormFile` and `UploadFormFiles` too.
|
||||||
|
func (ctx *Context) SaveFormFile(fh *multipart.FileHeader, dest string) (int64, error) {
|
||||||
src, err := fh.Open()
|
src, err := fh.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer src.Close()
|
defer src.Close()
|
||||||
|
|
||||||
out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename),
|
out, err := os.Create(dest)
|
||||||
os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -2204,6 +2212,35 @@ var (
|
||||||
// Usage: errors.Is(err, ErrEmptyFormField)
|
// Usage: errors.Is(err, ErrEmptyFormField)
|
||||||
// See postValue method. It's only returned on parsed post value methods.
|
// See postValue method. It's only returned on parsed post value methods.
|
||||||
ErrEmptyFormField = errors.New("empty form field")
|
ErrEmptyFormField = errors.New("empty form field")
|
||||||
|
|
||||||
|
// ConnectionCloseErrorSubstr if at least one of the given
|
||||||
|
// substrings are found in a net.OpError:os.SyscallError error type
|
||||||
|
// on `IsErrConnectionReset` then the function will report true.
|
||||||
|
ConnectionCloseErrorSubstr = []string{
|
||||||
|
"broken pipe",
|
||||||
|
"connection reset by peer",
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrConnectionClosed reports whether the given "err"
|
||||||
|
// is caused because of a broken connection.
|
||||||
|
IsErrConnectionClosed = func(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if opErr, ok := err.(*net.OpError); ok {
|
||||||
|
if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
|
||||||
|
errStr := strings.ToLower(syscallErr.Error())
|
||||||
|
for _, s := range ConnectionCloseErrorSubstr {
|
||||||
|
if strings.Contains(errStr, s) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadForm binds the request body of a form to the "formObject".
|
// ReadForm binds the request body of a form to the "formObject".
|
||||||
|
|
|
@ -3,8 +3,8 @@ package recover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http/httputil"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
)
|
)
|
||||||
|
@ -14,13 +14,8 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRequestLogs(ctx *context.Context) string {
|
func getRequestLogs(ctx *context.Context) string {
|
||||||
var status, ip, method, path string
|
rawReq, _ := httputil.DumpRequest(ctx.Request(), false)
|
||||||
status = strconv.Itoa(ctx.GetStatusCode())
|
return string(rawReq)
|
||||||
path = ctx.Path()
|
|
||||||
method = ctx.Method()
|
|
||||||
ip = ctx.RemoteAddr()
|
|
||||||
// the date should be logged by iris' Logger, so we skip them
|
|
||||||
return fmt.Sprintf("%v %s %s %s", status, path, method, ip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new recover middleware,
|
// New returns a new recover middleware,
|
||||||
|
@ -30,7 +25,7 @@ func New() context.Handler {
|
||||||
return func(ctx *context.Context) {
|
return func(ctx *context.Context) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
if ctx.IsStopped() {
|
if ctx.IsStopped() { // handled by other middleware.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user