1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-09 13:05:56 +00:00

new feature: versioned controllers

Former-commit-id: c797e23c78b1e74bbe9ba56673f3a98f17f5e2f7
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-06-19 20:58:24 +03:00
parent 3f98b39632
commit 311b560717
13 changed files with 217 additions and 32 deletions

View File

@@ -78,6 +78,12 @@ type ControllerActivator struct {
// End-devs can change some properties of the *Route on the `BeforeActivator` by using the
// `GetRoute/GetRoutes(functionName)`.
routes map[string][]*router.Route
// BeginHandlers is a slice of middleware for this controller.
// These handlers will be prependend to each one of
// the route that this controller will register(Handle/HandleMany/struct methods)
// to the targeted Party.
// Look the `Use` method too.
BeginHandlers context.Handlers
// true if this controller listens and serves to websocket events.
servesWebsocket bool
@@ -199,6 +205,15 @@ func (c *ControllerActivator) GetRoutes(methodName string) []*router.Route {
return nil
}
// Use registers a middleware for this Controller.
// It appends one or more handlers to the `BeginHandlers`.
// It's like the `Party.Use` but specifically
// for the routes that this controller will register to the targeted `Party`.
func (c *ControllerActivator) Use(handlers ...context.Handler) *ControllerActivator {
c.BeginHandlers = append(c.BeginHandlers, handlers...)
return c
}
// Singleton returns new if all incoming clients' requests
// have the same controller instance.
// This is done automatically by iris to reduce the creation
@@ -343,6 +358,7 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
}
handler := c.handlerOf(path, funcName)
middleware = context.JoinHandlers(c.BeginHandlers, middleware)
// register the handler now.
routes := c.app.Router.HandleMany(method, path, append(middleware, handler)...)

View File

@@ -703,6 +703,31 @@ func TestControllerOverlapping(t *testing.T) {
m.Handle(new(publicController))
e := httptest.New(t, app)
e.GET("/").WithQuery("name", "kataras").Expect().Status(httptest.StatusOK).JSON().Equal(iris.Map{"id": 1})
e.GET("/").Expect().Status(httptest.StatusOK).JSON().Equal(iris.Map{"data": "things"})
e.GET("/").WithQuery("name", "kataras").Expect().Status(httptest.StatusOK).
JSON().Equal(iris.Map{"id": 1})
e.GET("/").Expect().Status(httptest.StatusOK).
JSON().Equal(iris.Map{"data": "things"})
}
type testControllerMethodHandlerBindStruct struct{}
type queryData struct {
Name string `json:"name" url:"name"`
}
func (*testControllerMethodHandlerBindStruct) Any(data queryData) queryData {
return data
}
func TestControllerMethodHandlerBindStruct(t *testing.T) {
app := iris.New()
New(app).Handle(new(testControllerMethodHandlerBindStruct))
data := iris.Map{"name": "kataras"}
e := httptest.New(t, app)
e.GET("/").WithQueryObject(data).Expect().Status(httptest.StatusOK).JSON().Equal(data)
e.PATCH("/").WithJSON(data).Expect().Status(httptest.StatusOK).JSON().Equal(data)
// more tests inside the hero package itself.
}

View File

@@ -34,6 +34,8 @@ type GRPC struct {
Strict bool
}
var _ Option = GRPC{}
// Apply parses the controller's methods and registers gRPC handlers to the application.
func (g GRPC) Apply(c *ControllerActivator) {
defer c.Activated()

View File

@@ -146,13 +146,25 @@ func (app *Application) Register(dependencies ...interface{}) *Application {
return app
}
// Option is an interface which does contain a single `Apply` method that accepts
// a `ControllerActivator`. It can be passed on `Application.Handle` method to
// mdoify the behavior right after the `BeforeActivation` state.
//
// See `GRPC` package-level structure too.
type Option interface {
Apply(*ControllerActivator)
type (
// Option is an interface which does contain a single `Apply` method that accepts
// a `ControllerActivator`. It can be passed on `Application.Handle` method to
// mdoify the behavior right after the `BeforeActivation` state.
//
// See `GRPC` package-level structure
// and `Version` package-level function too.
Option interface {
Apply(*ControllerActivator)
}
// OptionFunc is the functional type of `Option`.
// Read `Option` docs.
OptionFunc func(*ControllerActivator)
)
// Apply completes the `Option` interface.
func (opt OptionFunc) Apply(c *ControllerActivator) {
opt(c)
}
// Handle serves a controller for the current mvc application's Router.

38
mvc/versioning.go Normal file
View File

@@ -0,0 +1,38 @@
package mvc
import (
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/versioning"
)
// Version returns a valid `Option` that can be passed to the `Application.Handle` method.
// It requires a specific "version" constraint for a Controller,
// e.g. ">1, <=2" or just "1".
//
//
// Usage:
// m := mvc.New(dataRouter)
// m.Handle(new(v1Controller), mvc.Version("1"))
// m.Handle(new(v2Controller), mvc.Version("2.3"))
// m.Handle(new(v3Controller), mvc.Version(">=3, <4"))
// m.Handle(new(noVersionController))
//
// See the `versioning` package's documentation for more information on
// how the version is extracted from incoming requests.
//
// Note that this Option will set the route register rule to `RouteOverlap`.
func Version(version string) OptionFunc {
return func(c *ControllerActivator) {
c.Router().SetRegisterRule(router.RouteOverlap) // required for this feature.
c.Use(func(ctx context.Context) {
if !versioning.Match(ctx, version) {
ctx.StopExecution()
return
}
ctx.Next()
})
}
}