Skip to content

Commit 46a3a99

Browse files
committed
various improvements and new 'UseOnce' method - read HISTORY.md
1 parent 5d480dc commit 46a3a99

File tree

20 files changed

+147
-221
lines changed

20 files changed

+147
-221
lines changed

HISTORY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,10 @@ Response:
359359

360360
Other Improvements:
361361

362+
- `*versioning.Group` type is a full `Party` now.
363+
364+
- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead.
365+
362366
- Ability to register a view engine per group of routes or for the current chain of handlers through `Party.RegisterView` and `Context.ViewEngine` respectfully.
363367

364368
- Add [Blocks](_examples/view/template_blocks_0) template engine. <!-- Reminder for @kataras: follow https://github.com/flosch/pongo2/pull/236#issuecomment-668950566 discussion so we can get back on using the original pongo2 repository as they fixed the issue about an incompatible 3rd party package (although they need more fixes, that's why I commented there) -->
@@ -527,6 +531,8 @@ New Context Methods:
527531

528532
Breaking Changes:
529533

534+
- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`.
535+
- `versioning.RegisterGroups` is **removed** as it is no longer necessary.
530536
- `Configuration.RemoteAddrHeaders` from `map[string]bool` to `[]string`. If you used `With(out)RemoteAddrHeader` then you are ready to proceed without any code changes for that one.
531537
- `ctx.Gzip(boolean)` replaced with `ctx.CompressWriter(boolean) error`.
532538
- `ctx.GzipReader(boolean) error` replaced with `ctx.CompressReader(boolean) error`.

_examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
* [The `urlpath` tmpl func](view/template_html_3/main.go)
102102
* [The `url` tmpl func](view/template_html_4/main.go)
103103
* [Inject Data Between Handlers](view/context-view-data/main.go)
104+
* [Inject Engine Between Handlers](view/context-view-engine/main.go)
104105
* [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)
105106
* [Write to a custom `io.Writer`](view/write-to)
106107
* [Blocks](view/template_blocks_0)

_examples/mvc/versioned-controller/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func newApp() *iris.Application {
3131
m.Handle(new(v1Controller), mvc.Version("1"), mvc.Deprecated(opts)) // 1 or 1.0, 1.0.0 ...
3232
m.Handle(new(v2Controller), mvc.Version("2.3")) // 2.3 or 2.3.0
3333
m.Handle(new(v3Controller), mvc.Version(">=3, <4")) // 3, 3.x, 3.x.x ...
34-
m.Handle(new(noVersionController))
34+
m.Handle(new(noVersionController)) // or if missing it will respond with 501 version not found.
3535
}
3636

3737
return app

_examples/routing/versioning/main.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,16 @@ func examplePerRoute(app *iris.Application) {
4242
// Headers[1] = Accept-Version: "2"
4343
func examplePerParty(app *iris.Application) {
4444
usersAPI := app.Party("/api/users")
45+
// You can customize the way a version is extracting
46+
// via middleware, for example:
47+
// version url parameter, and, if it's missing we default it to "1".
48+
usersAPI.Use(func(ctx iris.Context) {
49+
versioning.SetVersion(ctx, ctx.URLParamDefault("version", "1"))
50+
ctx.Next()
51+
})
4552

4653
// version 1.
47-
usersAPIV1 := versioning.NewGroup(">= 1, < 2")
54+
usersAPIV1 := versioning.NewGroup(usersAPI, ">= 1, < 2")
4855
usersAPIV1.Get("/", func(ctx iris.Context) {
4956
ctx.Writef("v1 resource: /api/users handler")
5057
})
@@ -53,15 +60,13 @@ func examplePerParty(app *iris.Application) {
5360
})
5461

5562
// version 2.
56-
usersAPIV2 := versioning.NewGroup(">= 2, < 3")
63+
usersAPIV2 := versioning.NewGroup(usersAPI, ">= 2, < 3")
5764
usersAPIV2.Get("/", func(ctx iris.Context) {
5865
ctx.Writef("v2 resource: /api/users handler")
5966
})
6067
usersAPIV2.Post("/", func(ctx iris.Context) {
6168
ctx.Writef("v2 resource: /api/users post handler")
6269
})
63-
64-
versioning.RegisterGroups(usersAPI, versioning.NotFoundHandler, usersAPIV1, usersAPIV2)
6570
}
6671

6772
func catsVersionExactly1Handler(ctx iris.Context) {

context/context.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ var acquireGoroutines = func() interface{} {
303303
}
304304
305305
func (ctx *Context) Go(fn func(cancelCtx stdContext.Context)) (running int) {
306-
g := ctx.Values().GetOrSet(goroutinesContextKey, acquireGoroutines).(*goroutines)
306+
g := ctx.values.GetOrSet(goroutinesContextKey, acquireGoroutines).(*goroutines)
307307
if fn != nil {
308308
g.wg.Add(1)
309309
@@ -613,6 +613,18 @@ func (ctx *Context) StopWithError(statusCode int, err error) {
613613
ctx.StopWithText(statusCode, err.Error())
614614
}
615615

616+
// StopWithPlainError like `StopWithError` but it does NOT
617+
// write anything to the response writer, it stores the error
618+
// so any error handler matching the given "statusCode" can handle it by its own.
619+
func (ctx *Context) StopWithPlainError(statusCode int, err error) {
620+
if err == nil {
621+
return
622+
}
623+
624+
ctx.SetErr(err)
625+
ctx.StopWithStatus(statusCode)
626+
}
627+
616628
// StopWithJSON stops the handlers chain, writes the status code
617629
// and sends a JSON response.
618630
//
@@ -4459,7 +4471,7 @@ func (ctx *Context) Exec(method string, path string) {
44594471
// backup the request path information
44604472
backupPath := req.URL.Path
44614473
backupMethod := req.Method
4462-
// don't backupValues := ctx.Values().ReadOnly()
4474+
// don't backupValues := ctx.values.ReadOnly()
44634475
// set the request to be align with the 'againstRequestPath'
44644476
req.RequestURI = path
44654477
req.URL.Path = path
@@ -4548,7 +4560,7 @@ func (ctx *Context) RegisterDependency(v interface{}) {
45484560
val = reflect.ValueOf(v)
45494561
}
45504562

4551-
cv := ctx.Values().Get(DependenciesContextKey)
4563+
cv := ctx.values.Get(DependenciesContextKey)
45524564
if cv != nil {
45534565
m, ok := cv.(DependenciesMap)
45544566
if !ok {
@@ -4559,15 +4571,15 @@ func (ctx *Context) RegisterDependency(v interface{}) {
45594571
return
45604572
}
45614573

4562-
ctx.Values().Set(DependenciesContextKey, DependenciesMap{
4574+
ctx.values.Set(DependenciesContextKey, DependenciesMap{
45634575
val.Type(): val,
45644576
})
45654577
}
45664578

45674579
// UnregisterDependency removes a dependency based on its type.
45684580
// Reports whether a dependency with that type was found and removed successfully.
45694581
func (ctx *Context) UnregisterDependency(typ reflect.Type) bool {
4570-
cv := ctx.Values().Get(DependenciesContextKey)
4582+
cv := ctx.values.Get(DependenciesContextKey)
45714583
if cv != nil {
45724584
m, ok := cv.(DependenciesMap)
45734585
if ok {
@@ -4594,18 +4606,25 @@ const errorContextKey = "iris.context.error"
45944606
// as a context value, it does nothing more.
45954607
// Also, by-default this error's value is written to the client
45964608
// on failures when no registered error handler is available (see `Party.On(Any)ErrorCode`).
4597-
// See `GetError` to retrieve it back.
4609+
// See `GetErr` to retrieve it back.
4610+
//
4611+
// To remove an error simply pass nil.
45984612
//
45994613
// Note that, if you want to stop the chain
4600-
// with an error see the `StopWithError` instead.
4614+
// with an error see the `StopWithError/StopWithPlainError` instead.
46014615
func (ctx *Context) SetErr(err error) {
4602-
ctx.Values().Set(errorContextKey, err)
4616+
if err == nil {
4617+
ctx.values.Remove(errorContextKey)
4618+
return
4619+
}
4620+
4621+
ctx.values.Set(errorContextKey, err)
46034622
}
46044623

46054624
// GetErr is a helper which retrieves
46064625
// the error value stored by `SetErr`.
46074626
func (ctx *Context) GetErr() error {
4608-
if v := ctx.Values().Get(errorContextKey); v != nil {
4627+
if v := ctx.values.Get(errorContextKey); v != nil {
46094628
if err, ok := v.(error); ok {
46104629
return err
46114630
}

core/router/api_builder.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ func overlapRoute(r *Route, next *Route) {
138138
return
139139
}
140140

141+
ctx.SetErr(nil) // clear any stored error.
142+
// Set the route to the next one and execute it.
141143
ctx.SetCurrentRoute(next.ReadOnly)
142144
ctx.HandlerIndex(0)
143145
ctx.Do(nextHandlers)
@@ -768,6 +770,25 @@ func (api *APIBuilder) Use(handlers ...context.Handler) {
768770
api.middleware = append(api.middleware, handlers...)
769771
}
770772

773+
// UseOnce either inserts a middleware,
774+
// or on the basis of the middleware already existing,
775+
// replace that existing middleware instead.
776+
func (api *APIBuilder) UseOnce(handlers ...context.Handler) {
777+
reg:
778+
for _, handler := range handlers {
779+
name := context.HandlerName(handler)
780+
for i, registeredHandler := range api.middleware {
781+
registeredName := context.HandlerName(registeredHandler)
782+
if name == registeredName {
783+
api.middleware[i] = handler // replace this handler with the new one.
784+
continue reg // break and continue to the next handler.
785+
}
786+
}
787+
788+
api.middleware = append(api.middleware, handler) // or just insert it.
789+
}
790+
}
791+
771792
// UseGlobal registers handlers that should run at the very beginning.
772793
// It prepends those handler(s) to all routes,
773794
// including all parties, subdomains.

core/router/fs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type Attachments struct {
4646
type DirCacheOptions struct {
4747
// Enable or disable cache.
4848
Enable bool
49-
// Minimium content size for compression in bytes.
49+
// Minimum content size for compression in bytes.
5050
CompressMinSize int64
5151
// Ignore compress files that match this pattern.
5252
CompressIgnore *regexp.Regexp

core/router/party.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,14 @@ type Party interface {
7676
// Use appends Handler(s) to the current Party's routes and child routes.
7777
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
7878
Use(middleware ...context.Handler)
79+
// UseOnce either inserts a middleware,
80+
// or on the basis of the middleware already existing,
81+
// replace that existing middleware instead.
82+
UseOnce(handlers ...context.Handler)
7983
// Done appends to the very end, Handler(s) to the current Party's routes and child routes.
8084
// The difference from .Use is that this/or these Handler(s) are being always running last.
8185
Done(handlers ...context.Handler)
86+
8287
// Reset removes all the begin and done handlers that may derived from the parent party via `Use` & `Done`,
8388
// and the execution rules.
8489
// Note that the `Reset` will not reset the handlers that are registered via `UseGlobal` & `DoneGlobal`.

mvc/versioning.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,8 @@ import (
2525
func Version(version string) OptionFunc {
2626
return func(c *ControllerActivator) {
2727
c.Router().SetRegisterRule(router.RouteOverlap) // required for this feature.
28-
29-
c.Use(func(ctx *context.Context) {
30-
if !versioning.Match(ctx, version) {
31-
ctx.StopExecution()
32-
return
33-
}
34-
35-
ctx.Next()
36-
})
28+
// Note: Do not use a group, we need c.Use for the specific controller's routes.
29+
c.Use(versioning.Handler(version))
3730
}
3831
}
3932

0 commit comments

Comments
 (0)