diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 54e3725a..00000000 --- a/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM irisgo/cloud-native-go:latest - -ENV APPSOURCES /go/src/github.com/iris-contrib/cloud-native-go - -RUN ${APPSOURCES}/cloud-native-go \ No newline at end of file diff --git a/Dockerfile.build b/Dockerfile.build deleted file mode 100644 index 6c127b0e..00000000 --- a/Dockerfile.build +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.9.3-alpine - -RUN apk update && apk upgrade && apk add --no-cache bash git -RUN go get github.com/iris-contrib/cloud-native-go - -ENV SOURCES /go/src/github.com/iris-contrib/cloud-native-go -# COPY . ${SOURCES} - -RUN cd ${SOURCES} $$ CGO_ENABLED=0 go build - -ENTRYPOINT cloud-native-go -EXPOSE 8080 \ No newline at end of file diff --git a/_examples/README.md b/_examples/README.md index 4caf6d3d..e0b00d67 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -146,8 +146,11 @@ Navigate through examples for a better understanding. - [Custom HTTP Errors](routing/http-errors/main.go) - [Dynamic Path](routing/dynamic-path/main.go) * [root level wildcard path](routing/dynamic-path/root-wildcard/main.go) +- [Write your own custom parameter types](routing/macros/main.go) **NEW** - [Reverse routing](routing/reverse/main.go) -- [Custom wrapper](routing/custom-wrapper/main.go) +- [Custom Router (high-level)](routing/custom-high-level-router/main.go) **NEW** +- [Custom Router (low-level)](routing/custom-low-level-router/main.go) **NEW** +- [Custom Wrapper](routing/custom-wrapper/main.go) - Custom Context * [method overriding](routing/custom-context/method-overriding/main.go) * [new implementation](routing/custom-context/new-implementation/main.go) diff --git a/_examples/README_ZH.md b/_examples/README_ZH.md index a10ca6ad..82b5648e 100644 --- a/_examples/README_ZH.md +++ b/_examples/README_ZH.md @@ -105,7 +105,10 @@ app.Get("{root:path}", rootWildcardHandler) - [自定义 HTTP 错误](routing/http-errors/main.go) - [动态路径](routing/dynamic-path/main.go) * [根级通配符路径](routing/dynamic-path/root-wildcard/main.go) +- [Write your own custom parameter types](routing/macros/main.go) **NEW** - [反向路由](routing/reverse/main.go) +- [Custom Router (high-level)](routing/custom-high-level-router/main.go) **NEW** +- [Custom Router (low-level)](routing/custom-low-level-router/main.go) **NEW** - [自定义包装](routing/custom-wrapper/main.go) - 自定义上下文    * [方法重写](routing/custom-context/method-overriding/main.go) diff --git a/_examples/routing/custom-high-level-router/main.go b/_examples/routing/custom-high-level-router/main.go new file mode 100644 index 00000000..1557c311 --- /dev/null +++ b/_examples/routing/custom-high-level-router/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "strings" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" +) + +/* A Router should contain all three of the following methods: + - HandleRequest should handle the request based on the Context. + HandleRequest(ctx context.Context) + - Build should builds the handler, it's being called on router's BuildRouter. + Build(provider router.RoutesProvider) error + - RouteExists reports whether a particular route exists. + RouteExists(ctx context.Context, method, path string) bool + +For a more detailed, complete and useful example +you can take a look at the iris' router itself which is located at: +https://github.com/kataras/iris/tree/master/core/router/handler.go +which completes this exact interface, the `router#RequestHandler`. +*/ +type customRouter struct { + // a copy of routes (safer because you will not be able to alter a route on serve-time without a `app.RefreshRouter` call): + // []router.Route + // or just expect the whole routes provider: + provider router.RoutesProvider +} + +// HandleRequest a silly example which finds routes based only on the first part of the requested path +// which must be a static one as well, the rest goes to fill the parameters. +func (r *customRouter) HandleRequest(ctx context.Context) { + path := ctx.Path() + ctx.Application().Logger().Infof("Requested resource path: %s", path) + + parts := strings.Split(path, "/")[1:] + staticPath := "/" + parts[0] + for _, route := range r.provider.GetRoutes() { + if strings.HasPrefix(route.Path, staticPath) { + paramParts := parts[1:] + for _, paramValue := range paramParts { + for _, p := range route.Tmpl().Params { + ctx.Params().Set(p.Name, paramValue) + } + } + + ctx.SetCurrentRouteName(route.Name) + ctx.Do(route.Handlers) + return + } + } + + // if nothing found... + ctx.StatusCode(iris.StatusNotFound) +} + +func (r *customRouter) Build(provider router.RoutesProvider) error { + for _, route := range provider.GetRoutes() { + // do any necessary validation or conversations based on your custom logic here + // but always run the "BuildHandlers" for each registered route. + route.BuildHandlers() + // [...] r.routes = append(r.routes, *route) + } + + r.provider = provider + return nil +} + +func (r *customRouter) RouteExists(ctx context.Context, method, path string) bool { + // [...] + return false +} + +func main() { + app := iris.New() + + // In case you are wondering, the parameter types and macros like "{param:string $func()}" still work inside + // your custom router if you fetch by the Route's Handler + // because they are middlewares under the hood, so you don't have to implement the logic of handling them manually, + // though you have to match what requested path is what route and fill the ctx.Params(), this is the work of your custom router. + app.Get("/hello/{name}", func(ctx context.Context) { + name := ctx.Params().Get("name") + ctx.Writef("Hello %s\n", name) + }) + + app.Get("/cs/{num:uint64 min(10) else 400}", func(ctx context.Context) { + num := ctx.Params().GetUint64Default("num", 0) + ctx.Writef("num is: %d\n", num) + }) + + // To replace the existing router with a customized one by using the iris/context.Context + // you have to use the `app.BuildRouter` method before `app.Run` and after the routes registered. + // You should pass your custom router's instance as the second input arg, which must completes the `router#RequestHandler` + // interface as shown above. + // + // To see how you can build something even more low-level without direct iris' context support (you can do that manually as well) + // navigate to the "custom-wrapper" example instead. + myCustomRouter := new(customRouter) + app.BuildRouter(app.ContextPool, myCustomRouter, app.APIBuilder, true) + + app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed)) +} diff --git a/_examples/routing/custom-low-level-router/main.go b/_examples/routing/custom-low-level-router/main.go new file mode 100644 index 00000000..d1aac6ed --- /dev/null +++ b/_examples/routing/custom-low-level-router/main.go @@ -0,0 +1,6 @@ +/// TODO: showcase the `app.Downgrade` feature tomorrow if not already existing elsewhere. +package main + +func main() { + panic("TODO") +} diff --git a/core/router/handler.go b/core/router/handler.go index 24ecfc5d..f16e1df0 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -17,10 +17,9 @@ import ( // RequestHandler the middle man between acquiring a context and releasing it. // By-default is the router algorithm. type RequestHandler interface { - // HandleRequest is same as context.Handler but its usage is only about routing, - // separate the concept here. + // HandleRequest should handle the request based on the Context. HandleRequest(context.Context) - // Build should builds the handler, it's being called on router's BuildRouter. + // Build should builds the handler, it's being called on router's BuildRouter. Build(provider RoutesProvider) error // RouteExists reports whether a particular route exists. RouteExists(ctx context.Context, method, path string) bool diff --git a/core/router/router.go b/core/router/router.go index 50526395..f4e9840d 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -31,7 +31,7 @@ func NewRouter() *Router { return &Router{} } // RefreshRouter re-builds the router. Should be called when a route's state // changed (i.e Method changed at serve-time). func (router *Router) RefreshRouter() error { - return router.BuildRouter(router.cPool, router.requestHandler, router.routesProvider) + return router.BuildRouter(router.cPool, router.requestHandler, router.routesProvider, true) } // BuildRouter builds the router based on @@ -41,7 +41,7 @@ func (router *Router) RefreshRouter() error { // its wrapper. // // Use of RefreshRouter to re-build the router if needed. -func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHandler, routesProvider RoutesProvider) error { +func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHandler, routesProvider RoutesProvider, force bool) error { if requestHandler == nil { return errors.New("router: request handler is nil") @@ -60,9 +60,23 @@ func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHan defer router.mu.Unlock() // store these for RefreshRouter's needs. - router.cPool = cPool - router.requestHandler = requestHandler - router.routesProvider = routesProvider + if force { + router.cPool = cPool + router.requestHandler = requestHandler + router.routesProvider = routesProvider + } else { + if router.cPool == nil { + router.cPool = cPool + } + + if router.requestHandler == nil { + router.requestHandler = requestHandler + } + + if router.routesProvider == nil && routesProvider != nil { + router.routesProvider = routesProvider + } + } // the important router.mainHandler = func(w http.ResponseWriter, r *http.Request) { diff --git a/iris.go b/iris.go index 8cab4999..5660847b 100644 --- a/iris.go +++ b/iris.go @@ -761,7 +761,7 @@ func (app *Application) Build() error { // create the request handler, the default routing handler routerHandler := router.NewDefaultHandler() - rp.Describe("router: %v", app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder)) + rp.Describe("router: %v", app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false)) // re-build of the router from outside can be done with; // app.RefreshRouter() }