add a simple URLParamSlice

I would love to change the current URLParams() to URLParams(name string) []string to match the PostValues(name string) []string, error but that would be a big breaking changes to many servers... so stick with it.
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-08-31 05:07:55 +03:00
parent 5f0a43cbc0
commit d0a27d2c08
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
4 changed files with 56 additions and 11 deletions

View File

@ -599,6 +599,7 @@ New Package-level Variables:
New Context Methods:
- `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.URLParamsSorted() []memstore.StringEntry` returns a sorted (by key) slice of key-value entries of the URL Query parameters.
- `Context.ViewEngine(ViewEngine)` to set a view engine on-fly for the current chain of handlers, responsible to render templates through `ctx.View`. [Example](_examples/view/context-view-engine).

View File

@ -25,6 +25,12 @@ func main() {
ctx.Writef("MyType: %#v", t)
})
app.Get("/simple", func(ctx iris.Context) {
names := ctx.URLParamSlice("name")
ctx.Writef("names: %v", ids)
})
// http://localhost:8080?name=iris&age=3
// http://localhost:8080/simple?name=john&name=doe&name=kataras
app.Listen(":8080")
}

View File

@ -117,7 +117,7 @@ type Context struct {
// the local key-value storage
params RequestParams // url named parameters.
values memstore.Store // generic storage, middleware communication.
query url.Values // GET url query temp cache, useful on many URLParamXXX calls.
// the underline application app.
app Application
// the route's handlers
@ -158,10 +158,16 @@ func (ctx *Context) Clone() *Context {
paramsCopy := make(memstore.Store, len(ctx.params.Store))
copy(paramsCopy, ctx.params.Store)
queryCopy := make(url.Values, len(ctx.query))
for k, v := range ctx.query {
queryCopy[k] = v
}
return &Context{
app: ctx.app,
values: valuesCopy,
params: RequestParams{Store: paramsCopy},
query: queryCopy,
writer: ctx.writer.Clone(),
request: ctx.request,
currentHandlerIndex: stopExecutionIndex,
@ -185,6 +191,7 @@ func (ctx *Context) BeginRequest(w http.ResponseWriter, r *http.Request) {
ctx.handlers = nil // will be filled by router.Serve/HTTP
ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
ctx.params.Store = ctx.params.Store[0:0]
ctx.query = nil
ctx.request = r
ctx.currentHandlerIndex = 0
ctx.proceeded = 0
@ -1363,19 +1370,23 @@ func (ctx *Context) GetStatusCode() int {
// | Various Request and Post Data |
// +------------------------------------------------------------+
// URLParamExists returns true if the url parameter exists, otherwise false.
func (ctx *Context) URLParamExists(name string) bool {
if q := ctx.request.URL.Query(); q != nil {
_, exists := q[name]
return exists
func (ctx *Context) getQuery() url.Values {
if ctx.query == nil {
ctx.query = ctx.request.URL.Query()
}
return false
return ctx.query
}
// URLParamExists returns true if the url parameter exists, otherwise false.
func (ctx *Context) URLParamExists(name string) bool {
_, exists := ctx.getQuery()[name]
return exists
}
// URLParamDefault returns the get parameter from a request, if not found then "def" is returned.
func (ctx *Context) URLParamDefault(name string, def string) string {
if v := ctx.request.URL.Query().Get(name); v != "" {
if v := ctx.getQuery().Get(name); v != "" {
return v
}
@ -1387,6 +1398,16 @@ func (ctx *Context) URLParam(name string) string {
return ctx.URLParamDefault(name, "")
}
// URLParamSlice a shortcut of ctx.Request().URL.Query()[name].
// Like `URLParam` but it returns all values instead of a single string separated by commas.
// Returns the values of a url query of the given "name" as string slice, e.g.
// ?name=john&name=doe&name=kataras will return [ john doe kataras].
//
// See `URLParamsSorted` for sorted values.
func (ctx *Context) URLParamSlice(name string) []string {
return ctx.getQuery()[name]
}
// URLParamTrim returns the url query parameter with trailing white spaces removed from a request.
func (ctx *Context) URLParamTrim(name string) string {
return strings.TrimSpace(ctx.URLParam(name))
@ -1527,7 +1548,7 @@ func (ctx *Context) URLParamBool(name string) (bool, error) {
//
// See URLParamsSorted too.
func (ctx *Context) URLParams() map[string]string {
q := ctx.request.URL.Query()
q := ctx.getQuery()
values := make(map[string]string, len(q))
for k, v := range q {
@ -1540,7 +1561,7 @@ func (ctx *Context) URLParams() map[string]string {
// URLParamsSorted returns a sorted (by key) slice
// of key-value entries of the URL Query parameters.
func (ctx *Context) URLParamsSorted() []memstore.StringEntry {
q := ctx.request.URL.Query()
q := ctx.getQuery()
n := len(q)
if n == 0 {
return nil
@ -1565,6 +1586,12 @@ func (ctx *Context) URLParamsSorted() []memstore.StringEntry {
return entries
}
// ResetQuery resets the GET URL Query cache.
// New URLParamXXX methods will receive the new parsed values.
func (ctx *Context) ResetQuery() {
ctx.query = nil
}
// No need anymore, net/http checks for the Form already.
// func (ctx *Context) askParseForm() error {
// if ctx.request.Form == nil {
@ -1706,6 +1733,10 @@ func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[str
func (ctx *Context) PostValues(name string) ([]string, error) {
_, ok := ctx.form()
if !ok {
if !ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {
return nil, nil
}
return nil, ErrEmptyForm // empty form.
}
@ -2208,7 +2239,7 @@ func (ctx *Context) ReadForm(formObject interface{}) error {
//
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-query/main.go
func (ctx *Context) ReadQuery(ptr interface{}) error {
values := ctx.request.URL.Query()
values := ctx.getQuery()
if len(values) == 0 {
return nil
}

View File

@ -990,6 +990,7 @@ func (api *APIBuilder) UseError(handlers ...context.Handler) {
// Use appends Handler(s) to the current Party's routes and child routes.
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
// The given "handlers" will be executed only on matched routes.
//
// Call order matters, it should be called right before the routes that they care about these handlers.
//
@ -1015,6 +1016,9 @@ func (api *APIBuilder) UseOnce(handlers ...context.Handler) {
// It doesn't care about call order, it will prepend the handlers to all
// existing routes and the future routes that may being registered.
//
// The given "handlers" will be executed only on matched routes and registered errors.
// See `UseRouter` if you want to register middleware that will always run, even on 404 not founds.
//
// The difference from `.DoneGlobal` is that this/or these Handler(s) are being always running first.
// Use of `ctx.Next()` of those handler(s) is necessary to call the main handler or the next middleware.
// It's always a good practise to call it right before the `Application#Run` function.
@ -1031,6 +1035,7 @@ func (api *APIBuilder) UseGlobal(handlers ...context.Handler) {
}
// Done appends to the very end, Handler(s) to the current Party's routes and child routes.
// The given "handlers" will be executed only on matched routes.
//
// Call order matters, it should be called right before the routes that they care about these handlers.
//
@ -1045,6 +1050,8 @@ func (api *APIBuilder) Done(handlers ...context.Handler) {
// It doesn't care about call order, it will append the handlers to all
// existing routes and the future routes that may being registered.
//
// The given "handlers" will be executed only on matched and registered error routes.
//
// The difference from `.UseGlobal` is that this/or these Handler(s) are being always running last.
// Use of `ctx.Next()` at the previous handler is necessary.
// It's always a good practise to call it right before the `Application#Run` function.