Update to 8.3.2 | Read HISTORY.md file

Former-commit-id: e6ab761989d596cb004c39e65e04e8968d9461ab
This commit is contained in:
kataras 2017-08-22 13:00:24 +03:00
parent 33e651866e
commit e12513a534
12 changed files with 150 additions and 13 deletions

View File

@ -18,6 +18,46 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`. **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
# Tu, 22 August 2017 | v8.3.2
### MVC
When one or more values of handler type (`func(ctx context.Context)`) are passed
right to the controller initialization then they will be recognised and act as middleware(s)
that ran even before the controller activation, there is no reason to load
the whole controller if the main handler or its `BeginRequest` are not "allowed" to be executed.
Example Code
```go
func checkLogin(ctx context.Context) {
if !myCustomAuthMethodPassed {
// [set a status or redirect, you know what to do]
ctx.StatusCode(iris.StatusForbidden)
return
}
// [continue to the next handler, at this example is our controller itself]
ctx.Next()
}
// [...]
app.Controller(new(ProfileController), checkLogin)
// [...]
```
Usage of these kind of MVC features could be found at the [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go#L174) source file.
### Other minor enhancements
- fix https://github.com/kataras/iris/issues/726[*](https://github.com/kataras/iris/commit/5e435fc54fe3dbf95308327c2180d1b444ef7e0d)
- fix redis sessiondb expiration[*](https://github.com/kataras/iris/commit/85cfc91544c981e87e09c5aa86bad4b85d0b96d3)
- update recursively when new version is available[*](https://github.com/kataras/iris/commit/cd3c223536c6a33653a7fcf1f0648123f2b968fd)
- some minor session enhancements[*](https://github.com/kataras/iris/commit/2830f3b50ee9c526ac792c3ce1ec1c08c24ea024)
# Sa, 19 August 2017 | v8.3.1 # Sa, 19 August 2017 | v8.3.1
First of all I want to thank you for the 100% green feedback you gratefully sent me you about First of all I want to thank you for the 100% green feedback you gratefully sent me you about

View File

@ -38,7 +38,7 @@ Iris may have reached version 8, but we're not stopping there. We have many feat
### 📑 Table of contents ### 📑 Table of contents
* [Installation](#-installation) * [Installation](#-installation)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-19-august-2017--v831) * [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-22-august-2017--v832)
* [Learn](#-learn) * [Learn](#-learn)
* [HTTP Listening](_examples/#http-listening) * [HTTP Listening](_examples/#http-listening)
* [Configuration](_examples/#configuration) * [Configuration](_examples/#configuration)

View File

@ -1 +1 @@
8.3.1:https://github.com/kataras/iris/blob/master/HISTORY.md#sa-19-august-2017--v831 8.3.2:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-22-august-2017--v832

View File

@ -220,7 +220,7 @@ Iris Application written using **14 code of lines** ran for **8 seconds** servin
## Sessions ## Sessions
Spawn `5000000 requests` with 125 different "threads" targeting a static request path, sets and gets a session based on the name `"key"` and string value `"value"` and write tat session value to the response stream. Spawn `5000000 requests` with 125 different "threads" targeting a static request path, sets and gets a session based on the name `"key"` and string value `"value"` and write that session value to the response stream.
### .NET Core (Kestrel) with Sessions ### .NET Core (Kestrel) with Sessions

View File

@ -492,15 +492,16 @@ func (api *APIBuilder) Any(relativePath string, handlers ...context.Handler) (ro
// } // }
// //
// Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now()) // Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now())
// Note: Binded values of context.Handler type are being recognised as middlewares by the router.
// //
// Read more at `/mvc#Controller`. // Read more at `/mvc#Controller`.
func (api *APIBuilder) Controller(relativePath string, controller activator.BaseController, func (api *APIBuilder) Controller(relativePath string, controller activator.BaseController,
bindValues ...interface{}) (routes []*Route) { bindValues ...interface{}) (routes []*Route) {
registerFunc := func(method string, handler context.Handler) { registerFunc := func(method string, handlers ...context.Handler) {
if method == "ANY" || method == "ALL" { if method == "ANY" || method == "ALL" {
routes = api.Any(relativePath, handler) routes = api.Any(relativePath, handlers...)
} else { } else {
routes = append(routes, api.HandleMany(method, relativePath, handler)...) routes = append(routes, api.HandleMany(method, relativePath, handlers...)...)
} }
} }

View File

@ -169,6 +169,7 @@ type Party interface {
// } // }
// //
// Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now()) // Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now())
// Note: Binded values of context.Handler type are being recognised as middlewares by the router.
// //
// Read more at `/mvc#Controller`. // Read more at `/mvc#Controller`.
Controller(relativePath string, controller activator.BaseController, bindValues ...interface{}) []*Route Controller(relativePath string, controller activator.BaseController, bindValues ...interface{}) []*Route

2
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
8.3.1 8.3.2
Installation Installation

View File

@ -32,7 +32,7 @@ import (
const ( const (
// Version is the current version number of the Iris Web Framework. // Version is the current version number of the Iris Web Framework.
Version = "8.3.1" Version = "8.3.2"
) )
// HTTP status codes as registered with IANA. // HTTP status codes as registered with IANA.

View File

@ -277,7 +277,7 @@ func buildMethodHandler(t TController, methodFuncIndex int) context.Handler {
} }
// RegisterFunc used by the caller to register the result routes. // RegisterFunc used by the caller to register the result routes.
type RegisterFunc func(httpMethod string, handler context.Handler) type RegisterFunc func(httpMethod string, handler ...context.Handler)
// RegisterMethodHandlers receives a `TController`, description of the // RegisterMethodHandlers receives a `TController`, description of the
// user's controller, and calls the "registerFunc" for each of its // user's controller, and calls the "registerFunc" for each of its
@ -292,8 +292,19 @@ func RegisterMethodHandlers(t TController, registerFunc RegisterFunc) {
// http methods using the registerFunc, which is // http methods using the registerFunc, which is
// responsible to convert these into routes // responsible to convert these into routes
// and add them to router via the APIBuilder. // and add them to router via the APIBuilder.
var handlers context.Handlers
if t.binder != nil {
if m := t.binder.middleware; len(m) > 0 {
handlers = append(handlers, t.binder.middleware...)
}
}
for _, m := range t.Methods { for _, m := range t.Methods {
registerFunc(m.HTTPMethod, buildMethodHandler(t, m.Index)) methodHandler := buildMethodHandler(t, m.Index)
registeredHandlers := append(handlers, methodHandler)
registerFunc(m.HTTPMethod, registeredHandlers...)
} }
} }

View File

@ -2,11 +2,17 @@ package activator
import ( import (
"reflect" "reflect"
"github.com/kataras/iris/context"
) )
type binder struct { type binder struct {
values []interface{} values []interface{}
fields []field fields []field
// saves any middleware that may need to be passed to the router,
// statically, to gain performance.
middleware context.Handlers
} }
// binder accepts a value of something // binder accepts a value of something
@ -26,19 +32,42 @@ func newBinder(elemType reflect.Type, values []interface{}) *binder {
// if nothing valid found return nil, so the caller // if nothing valid found return nil, so the caller
// can omit the binder. // can omit the binder.
if len(b.fields) == 0 { if len(b.fields) == 0 && len(b.middleware) == 0 {
return nil return nil
} }
return b return b
} }
func (b *binder) storeValueIfMiddleware(value reflect.Value) bool {
if value.CanInterface() {
if m, ok := value.Interface().(context.Handler); ok {
b.middleware = append(b.middleware, m)
return true
}
if m, ok := value.Interface().(func(context.Context)); ok {
b.middleware = append(b.middleware, m)
return true
}
}
return false
}
func (b *binder) lookup(elem reflect.Type) (fields []field) { func (b *binder) lookup(elem reflect.Type) (fields []field) {
for _, v := range b.values { for _, v := range b.values {
value := reflect.ValueOf(v) value := reflect.ValueOf(v)
// handlers will be recognised as middleware, not struct fields.
// End-Developer has the option to call any handler inside
// the controller's `BeginRequest` and `EndRequest`, the
// state is respected from the method handler already.
if b.storeValueIfMiddleware(value) {
// stored as middleware, continue to the next field, we don't have
// to bind anything here.
continue
}
for i, n := 0, elem.NumField(); i < n; i++ { for i, n := 0, elem.NumField(); i < n; i++ {
elemField := elem.Field(i) elemField := elem.Field(i)
if elemField.Type == value.Type() { if elemField.Type == value.Type() {
// we area inside the correct type // we area inside the correct type
// println("[0] prepare bind filed for " + elemField.Name) // println("[0] prepare bind filed for " + elemField.Name)
@ -111,6 +140,13 @@ func lookupStruct(elem reflect.Type, value reflect.Value) *field {
} }
func (b *binder) handle(c reflect.Value) { func (b *binder) handle(c reflect.Value) {
// we could make check for middlewares here but
// these could easly be used outside of the controller
// so we don't have to initialize a controller to call them
// so they don't belong actually here, we will register them to the
// router itself, before the controller's handler to gain performance,
// look `activator.go#RegisterMethodHandlers` for more.
elem := c.Elem() // controller should always be a pointer at this state elem := c.Elem() // controller should always be a pointer at this state
for _, f := range b.fields { for _, f := range b.fields {
f.sendTo(elem) f.sendTo(elem)

View File

@ -58,6 +58,7 @@ import (
// } // }
// //
// Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now()) // Usage: app.Controller("/user/{id:int}", new(UserController), db, time.Now())
// Note: Binded values of context.Handler type are being recognised as middlewares by the router.
// //
// Look `core/router/APIBuilder#Controller` method too. // Look `core/router/APIBuilder#Controller` method too.
type Controller struct { type Controller struct {
@ -140,7 +141,7 @@ func (c *Controller) RelPath() string {
reqPath := c.Ctx.Path() reqPath := c.Ctx.Path()
if len(reqPath) == 0 { if len(reqPath) == 0 {
// it never come here // it never come here
// but to protect ourselves jsut return an empty slash. // but to protect ourselves just return an empty slash.
return slashStr return slashStr
} }
// [1:]to ellimuate the prefixes like "//" // [1:]to ellimuate the prefixes like "//"

View File

@ -171,6 +171,53 @@ func TestControllerBeginAndEndRequestFunc(t *testing.T) {
} }
} }
func TestControllerBeginAndEndRequestFuncBindMiddleware(t *testing.T) {
app := iris.New()
usernames := map[string]bool{
"kataras": true,
"makis": false,
"efi": true,
"rg": false,
"bill": true,
"whoisyourdaddy": false,
}
middlewareCheck := func(ctx context.Context) {
for username, allow := range usernames {
if ctx.Params().Get("username") == username && allow {
ctx.Next()
return
}
}
ctx.StatusCode(httptest.StatusForbidden)
ctx.Writef("forbidden")
}
app.Controller("/profile/{username}", new(testControllerBeginAndEndRequestFunc), middlewareCheck)
e := httptest.New(t, app)
doneResponse := "done"
for username, allow := range usernames {
getEx := e.GET("/profile/" + username).Expect()
if allow {
getEx.Status(httptest.StatusOK).
Body().Equal(username + doneResponse)
} else {
getEx.Status(httptest.StatusForbidden).Body().Equal("forbidden")
}
postEx := e.POST("/profile/" + username).Expect()
if allow {
postEx.Status(httptest.StatusOK).
Body().Equal(username + doneResponse)
} else {
postEx.Status(httptest.StatusForbidden).Body().Equal("forbidden")
}
}
}
type Model struct { type Model struct {
Username string Username string
} }