mirror of
https://github.com/kataras/iris.git
synced 2025-03-13 21:36:28 +01:00
Update to 8.2.6 | More on Iris Controllers: optional EndRequest
. Read HISTORY.md
Former-commit-id: 5255ca5c4a898e2f3e7388f3af0457599e65f87f
This commit is contained in:
parent
8cd07719a6
commit
35620f6ecb
23
HISTORY.md
23
HISTORY.md
|
@ -18,6 +18,26 @@ 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`.
|
||||
|
||||
# Mo, 14 August 2017 | v8.2.6
|
||||
|
||||
Able to call done/end handlers inside a `Controller`, via optional `EndRequest(ctx context.Context)` function inside the controller struct.
|
||||
|
||||
```go
|
||||
// it's called after t.Get()/Post()/Put()/Delete()/Connect()/Head()/Patch()/Options()/Trace().
|
||||
func (t *testControllerEndRequestFunc) EndRequest(ctx context.Context) {
|
||||
// 2.
|
||||
// [your code goes here...]
|
||||
}
|
||||
|
||||
// will handle "GET" request HTTP method only.
|
||||
func (t *testControllerEndRequestFunc) Get() {
|
||||
// 1.
|
||||
// [your code goes here...]
|
||||
}
|
||||
```
|
||||
|
||||
Look at the [v8.2.5 changelog](#su-13-august-2017--v825) to learn more about the new Iris Controllers feature.
|
||||
|
||||
# Su, 13 August 2017 | v8.2.5
|
||||
|
||||
Good news for devs that are used to write their web apps using the `MVC-style` app architecture.
|
||||
|
@ -29,7 +49,8 @@ Our `Controller` supports many things among them are:
|
|||
|
||||
- all HTTP Methods are supported, for example if want to serve `GET` then the controller should have a function named `Get()`, you can define more than one method function to serve in the same Controller struct
|
||||
- `persistence` data inside your Controller struct (share data between requests) via **`iris:"persistence"`** tag right to the field
|
||||
- optional `Init` function to perform any initialization before the methods, useful to call middlewares or when many methods use the same collection of data
|
||||
- optional `Init(ctx) or BeginRequest(ctx)` function to perform any initialization before the methods, useful to call middlewares or when many methods use the same collection of data
|
||||
- optional `Done(ctx) or EndRequest(ctx)` function to perform any finalization after the methods executed
|
||||
- access to the request path parameters via the `Params` field
|
||||
- access to the template file that should be rendered via the `Tmpl` field
|
||||
- access to the template data that should be rendered inside the template file via `Data` field
|
||||
|
|
|
@ -26,7 +26,7 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
|
|||
### 📑 Table of contents
|
||||
|
||||
* [Installation](#-installation)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#su-13-august-2017--v825)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#mo-14-august-2017--v826)
|
||||
* [Learn](#-learn)
|
||||
* [HTTP Listening](_examples/#http-listening)
|
||||
* [Configuration](_examples/#configuration)
|
||||
|
|
|
@ -204,6 +204,7 @@ iris session manager lives on its own [package](https://github.com/kataras/iris/
|
|||
- [Databases](sessions/database)
|
||||
* [File](sessions/database/file/main.go)
|
||||
* [BoltDB](sessions/database/boltdb/main.go)
|
||||
* [LevelDB](sessions/database/leveldb/main.go)
|
||||
* [Redis](sessions/database/redis/main.go)
|
||||
|
||||
> You're free to use your own favourite sessions package if you'd like so.
|
||||
|
|
|
@ -42,7 +42,7 @@ func (c *User) Get() {
|
|||
}
|
||||
|
||||
/* Can use more than one, the factory will make sure
|
||||
that the correct http methods are being registed for this
|
||||
that the correct http methods are being registered for this
|
||||
controller, uncommend these if you want:
|
||||
|
||||
func (c *User) Post() {}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (c *User) Get() {
|
|||
}
|
||||
|
||||
/* Can use more than one, the factory will make sure
|
||||
that the correct http methods are being registed for this
|
||||
that the correct http methods are being registered for this
|
||||
controller, uncommend these if you want:
|
||||
|
||||
func (c *User) Post() {}
|
||||
|
|
|
@ -86,7 +86,7 @@ func main() {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
|
@ -82,7 +82,7 @@ func main() {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
|
@ -87,7 +87,7 @@ func main() {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
|
@ -76,7 +76,7 @@ func main() {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
|
@ -64,7 +64,7 @@ func newApp() *iris.Application {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
mySessions.ShiftExpiraton(ctx)
|
||||
mySessions.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Get("/destroy", func(ctx context.Context) {
|
||||
|
|
|
@ -71,7 +71,7 @@ func main() {
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Get("/destroy", func(ctx context.Context) {
|
||||
|
|
|
@ -76,7 +76,7 @@ func (c *UserController) Get() {
|
|||
}
|
||||
|
||||
/* Can use more than one, the factory will make sure
|
||||
that the correct http methods are being registed for this
|
||||
that the correct http methods are being registered for this
|
||||
controller, uncommend these if you want:
|
||||
|
||||
func (c *User) Post() {}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (c *User) Get() {
|
|||
}
|
||||
|
||||
/* Can use more than one, the factory will make sure
|
||||
that the correct http methods are being registed for this
|
||||
that the correct http methods are being registered for this
|
||||
controller, uncommend these if you want:
|
||||
|
||||
func (c *User) Post() {}
|
||||
|
|
|
@ -164,7 +164,8 @@ func registerController(p Party, path string, c interface{}) ([]*Route, error) {
|
|||
}
|
||||
}
|
||||
|
||||
customInitFuncIndex, _ := getCustomInitFuncIndex(typ)
|
||||
customInitFuncIndex, _ := getCustomFuncIndex(typ, customInitFuncNames...)
|
||||
customEndFuncIndex, _ := getCustomFuncIndex(typ, customEndFuncNames...)
|
||||
|
||||
// check if has Any() or All()
|
||||
// if yes, then register all http methods and
|
||||
|
@ -176,7 +177,7 @@ func registerController(p Party, path string, c interface{}) ([]*Route, error) {
|
|||
if has {
|
||||
routes := p.Any(path,
|
||||
controllerToHandler(elem, persistenceFields,
|
||||
baseControllerFieldIndex, m.Index, customInitFuncIndex))
|
||||
baseControllerFieldIndex, m.Index, customInitFuncIndex, customEndFuncIndex))
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
|
@ -196,14 +197,14 @@ func registerController(p Party, path string, c interface{}) ([]*Route, error) {
|
|||
|
||||
r := p.Handle(method, path,
|
||||
controllerToHandler(elem, persistenceFields,
|
||||
baseControllerFieldIndex, httpMethodIndex, customInitFuncIndex))
|
||||
baseControllerFieldIndex, httpMethodIndex, customInitFuncIndex, customEndFuncIndex))
|
||||
routes = append(routes, r)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func controllerToHandler(elem reflect.Type, persistenceFields map[int]reflect.Value,
|
||||
baseControllerFieldIndex, httpMethodIndex int, customInitFuncIndex int) context.Handler {
|
||||
baseControllerFieldIndex, httpMethodIndex, customInitFuncIndex, customEndFuncIndex int) context.Handler {
|
||||
return func(ctx context.Context) {
|
||||
// create a new controller instance of that type(>ptr).
|
||||
c := reflect.New(elem)
|
||||
|
@ -229,10 +230,10 @@ func controllerToHandler(elem reflect.Type, persistenceFields map[int]reflect.Va
|
|||
// init the new controller instance.
|
||||
b.init(ctx)
|
||||
|
||||
// calls the higher "Init(ctx context.Context)",
|
||||
// call the higher "Init/BeginRequest(ctx context.Context)",
|
||||
// if exists.
|
||||
if customInitFuncIndex > 0 {
|
||||
callCustomInit(ctx, c, customInitFuncIndex)
|
||||
if customInitFuncIndex >= 0 {
|
||||
callCustomFuncHandler(ctx, c, customInitFuncIndex)
|
||||
}
|
||||
|
||||
// if custom Init didn't stop the execution of the
|
||||
|
@ -242,22 +243,39 @@ func controllerToHandler(elem reflect.Type, persistenceFields map[int]reflect.Va
|
|||
methodFunc.Interface().(func())()
|
||||
}
|
||||
|
||||
if !ctx.IsStopped() {
|
||||
// call the higher "Done/EndRequest(ctx context.Context)",
|
||||
// if exists.
|
||||
if customEndFuncIndex >= 0 {
|
||||
callCustomFuncHandler(ctx, c, customEndFuncIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// finally, execute the controller.
|
||||
b.exec()
|
||||
}
|
||||
}
|
||||
|
||||
// Init can be used as a custom function
|
||||
// to init the new instance of controller
|
||||
// that is created on each new request.
|
||||
//
|
||||
// Useful when more than one methods are using the same
|
||||
// request data.
|
||||
const customInitFuncName = "Init"
|
||||
var (
|
||||
// customInitFuncNames can be used as custom functions
|
||||
// to init the new instance of controller
|
||||
// that is created on each new request.
|
||||
// One of these is valid, no both.
|
||||
customInitFuncNames = []string{"Init", "BeginRequest"}
|
||||
// customEndFuncNames can be used as custom functions
|
||||
// to action when the method handler has been finished,
|
||||
// this is the last step before server send the response to the client.
|
||||
// One of these is valid, no both.
|
||||
customEndFuncNames = []string{"Done", "EndRequest"}
|
||||
)
|
||||
|
||||
func getCustomInitFuncIndex(typ reflect.Type) (initFuncIndex int, has bool) {
|
||||
if m, has := typ.MethodByName(customInitFuncName); has {
|
||||
return m.Index, has
|
||||
func getCustomFuncIndex(typ reflect.Type, funcNames ...string) (initFuncIndex int, has bool) {
|
||||
for _, customInitFuncName := range funcNames {
|
||||
if m, has := typ.MethodByName(customInitFuncName); has {
|
||||
return m.Index, has
|
||||
}
|
||||
}
|
||||
|
||||
return -1, false
|
||||
|
@ -265,8 +283,8 @@ func getCustomInitFuncIndex(typ reflect.Type) (initFuncIndex int, has bool) {
|
|||
|
||||
// the "cServeTime" is a new "c" instance
|
||||
// which is being used at serve time, inside the Handler.
|
||||
// it calls the custom "Init", the check of this
|
||||
// function made at build time, so it's a safe a call.
|
||||
func callCustomInit(ctx context.Context, cServeTime reflect.Value, initFuncIndex int) {
|
||||
// it calls the custom function (can be "Init", "BeginRequest", "End" and "EndRequest"),
|
||||
// the check of this function made at build time, so it's a safe a call.
|
||||
func callCustomFuncHandler(ctx context.Context, cServeTime reflect.Value, initFuncIndex int) {
|
||||
cServeTime.Method(initFuncIndex).Interface().(func(ctx context.Context))(ctx)
|
||||
}
|
||||
|
|
|
@ -98,30 +98,38 @@ func TestControllerPersistenceFields(t *testing.T) {
|
|||
Body().Equal(data)
|
||||
}
|
||||
|
||||
type testControllerInitFunc struct {
|
||||
type testControllerBeginAndEndRequestFunc struct {
|
||||
router.Controller
|
||||
|
||||
Username string
|
||||
}
|
||||
|
||||
// called before of every method (Get() or Post()).
|
||||
//
|
||||
// useful when more than one methods using the
|
||||
// same request values or context's function calls.
|
||||
func (t *testControllerInitFunc) Init(ctx context.Context) {
|
||||
func (t *testControllerBeginAndEndRequestFunc) BeginRequest(ctx context.Context) {
|
||||
t.Username = ctx.Params().Get("username")
|
||||
// or t.Params.Get("username") because the
|
||||
// t.Ctx == ctx and is being initialized before this "Init"
|
||||
// t.Ctx == ctx and is being initialized before this "BeginRequest"
|
||||
}
|
||||
|
||||
func (t *testControllerInitFunc) Get() {
|
||||
// called after every method (Get() or Post()).
|
||||
func (t *testControllerBeginAndEndRequestFunc) EndRequest(ctx context.Context) {
|
||||
ctx.Writef("done") // append "done" to the response
|
||||
}
|
||||
|
||||
func (t *testControllerBeginAndEndRequestFunc) Get() {
|
||||
t.Ctx.Writef(t.Username)
|
||||
}
|
||||
|
||||
func (t *testControllerInitFunc) Post() {
|
||||
func (t *testControllerBeginAndEndRequestFunc) Post() {
|
||||
t.Ctx.Writef(t.Username)
|
||||
}
|
||||
func TestControllerInitFunc(t *testing.T) {
|
||||
|
||||
func TestControllerBeginAndEndRequestFunc(t *testing.T) {
|
||||
app := iris.New()
|
||||
app.Controller("/profile/{username}", new(testControllerInitFunc))
|
||||
app.Controller("/profile/{username}", new(testControllerBeginAndEndRequestFunc))
|
||||
|
||||
e := httptest.New(t, app)
|
||||
usernames := []string{
|
||||
|
@ -132,11 +140,13 @@ func TestControllerInitFunc(t *testing.T) {
|
|||
"bill",
|
||||
"whoisyourdaddy",
|
||||
}
|
||||
doneResponse := "done"
|
||||
|
||||
for _, username := range usernames {
|
||||
e.GET("/profile/" + username).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(username)
|
||||
Body().Equal(username + doneResponse)
|
||||
e.POST("/profile/" + username).Expect().Status(httptest.StatusOK).
|
||||
Body().Equal(username)
|
||||
Body().Equal(username + doneResponse)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
12
doc.go
12
doc.go
|
@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
|
|||
|
||||
Current Version
|
||||
|
||||
8.2.5
|
||||
8.2.6
|
||||
|
||||
Installation
|
||||
|
||||
|
@ -695,9 +695,6 @@ you can define more than one method function to serve in the same Controller str
|
|||
Persistence data inside your Controller struct (share data between requests)
|
||||
via `iris:"persistence"` tag right to the field.
|
||||
|
||||
Optional `Init` function to perform any initialization before the methods,
|
||||
useful to call middlewares or when many methods use the same collection of data.
|
||||
|
||||
Access to the request path parameters via the `Params` field.
|
||||
|
||||
Access to the template file that should be rendered via the `Tmpl` field.
|
||||
|
@ -712,6 +709,11 @@ Access to the low-level `context.Context` via the `Ctx` field.
|
|||
Flow as you used to, `Controllers` can be registered to any `Party`,
|
||||
including Subdomains, the Party's begin and done handlers work as expected.
|
||||
|
||||
Optional `Init(ctx) or BeginRequest(ctx)` function to perform any initialization before the methods,
|
||||
useful to call middlewares or when many methods use the same collection of data.
|
||||
|
||||
Optional `Done(ctx) or EndRequest(ctx)` function to perform any finalization after the methods executed.
|
||||
|
||||
Example Code:
|
||||
|
||||
|
||||
|
@ -1448,7 +1450,7 @@ Example Code:
|
|||
|
||||
app.Get("/update", func(ctx context.Context) {
|
||||
// updates expire date with a new date
|
||||
sess.ShiftExpiraton(ctx)
|
||||
sess.ShiftExpiration(ctx)
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
|
2
iris.go
2
iris.go
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
const (
|
||||
// Version is the current version number of the Iris Web Framework.
|
||||
Version = "8.2.5"
|
||||
Version = "8.2.6"
|
||||
)
|
||||
|
||||
// HTTP status codes as registered with IANA.
|
||||
|
|
|
@ -143,10 +143,10 @@ func (p *provider) Init(sid string, expires time.Duration) *Session {
|
|||
return newSession
|
||||
}
|
||||
|
||||
// UpdateExpiraton update expire date of a session.
|
||||
// UpdateExpiration update expire date of a session.
|
||||
// if expires > 0 then it updates the destroy task.
|
||||
// if expires <=0 then it does nothing, to destroy a session call the `Destroy` func instead.
|
||||
func (p *provider) UpdateExpiraton(sid string, expires time.Duration) bool {
|
||||
func (p *provider) UpdateExpiration(sid string, expires time.Duration) bool {
|
||||
if expires <= 0 {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -119,19 +119,19 @@ func (s *Sessions) Start(ctx context.Context) *Session {
|
|||
return sess
|
||||
}
|
||||
|
||||
// ShiftExpiraton move the expire date of a session to a new date
|
||||
// ShiftExpiration move the expire date of a session to a new date
|
||||
// by using session default timeout configuration.
|
||||
func (s *Sessions) ShiftExpiraton(ctx context.Context) {
|
||||
s.UpdateExpiraton(ctx, s.config.Expires)
|
||||
func (s *Sessions) ShiftExpiration(ctx context.Context) {
|
||||
s.UpdateExpiration(ctx, s.config.Expires)
|
||||
}
|
||||
|
||||
// UpdateExpiraton change expire date of a session to a new date
|
||||
// UpdateExpiration change expire date of a session to a new date
|
||||
// by using timeout value passed by `expires` receiver.
|
||||
func (s *Sessions) UpdateExpiraton(ctx context.Context, expires time.Duration) {
|
||||
func (s *Sessions) UpdateExpiration(ctx context.Context, expires time.Duration) {
|
||||
cookieValue := s.decodeCookieValue(GetCookie(ctx, s.config.Cookie))
|
||||
|
||||
if cookieValue != "" {
|
||||
if s.provider.UpdateExpiraton(cookieValue, expires) {
|
||||
if s.provider.UpdateExpiration(cookieValue, expires) {
|
||||
s.updateCookie(ctx, cookieValue, expires)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user