烂翻译系列之Iris——路由——中间件
Middleware 中间件
When we refer to the concept of Middleware within Iris, we're talking about running code before or after our main handler within an HTTP request's lifecycle. For example, a middleware called "logger" might write incoming request details to a file or the console, before proceeding with the main handler. One of the cool things about middleware is that these units are incredibly flexible and reusable within and across your applications!
在我们提到Iris的中间件这个概念,我们就是在谈论在一个HTTP请求生命周期内,在主处理器之前或之后运行的代码。例如,日志中间件,在请求到达主处理器之前,会将请求详情写入到文件或控制台。比较酷的是,中间件对于应用是松耦合和可复用的。
func(ctx iris.Context)
. Each middleware is executed when the previous middleware calls the ctx.Next()
method, which can be used for authentication, i.e., if the request isn't authorized to continue, we could throw an error response instead of calling this method.ctx.Next()
函数。Register A Middleware 注册中间件
Let's dive into the details of each one of the above methods through a quick lesson on how you can register middlewares in an Iris Application and Party (group of routes). Below you will see a simple example which outputs to the client the order of execution of each registered handler.
让我们通过简要介绍怎样在Iris应用和Party(一组路由)中注册中间件,深入上面每个函数的细节。下面你将看到一个简单示例,该示例向客户端输出每个已注册处理器的执行顺序。
package main import ( "net/http" "github.com/kataras/iris/v12" ) func main() { app := iris.New() app.WrapRouter(routerWrapper) app.UseRouter(routerMiddleware) app.UseGlobal(globalMiddleware) app.Use(useMiddleware) app.UseError(errorMiddleware) // app.Done(done) // app.DoneGlobal(doneGlobal) // Adding a OnErrorCode(iris.StatusNotFound) causes `.UseGlobal` // to be fired on 404 pages without this, // only `UseError` will be called, and thus should // be used for error pages.// app.OnErrorCode(iris.StatusNotFound, notFoundHandler) app.Get("/", mainHandler) app.Listen(":8080") } func mainHandler(ctx iris.Context) { ctx.WriteString("Main Handler") } func notFoundHandler(ctx iris.Context) { ctx.WriteString("404 Error Handler") }
The Application.WrapRouter
Method Application.WrapRouter函数
A WrapperFunc registered in this way runs for the whole Application, before anything else. It is executed last registered first, unlike other middleware methods. A WrapperFunc
is not an iris.Handler
, instead its signature is: func(http.ResponseWriter, *http.Request, router http.HandlerFunc)
. It's the lowest-level functionality used to intercept requests and optionally change the behaviour of the router (e.g. navigate to another route than the requested path one, perform security checks before even the router is executed and more). A good example of use-case is a CORS implementation.
以这种方式注册的WrapperFunc将在整个应用程序中,其他任何内容之前运行。它最先注册执行,不像其他中间件方法。 WrapperFunc
不是iris.Handler,而是它的签名是
:func(http.ResponseWriter, *http.Request, router http.HandlerFunc)。
它是用于拦截请求和选择性地更改路由器行为的最低级别的功能(例如,导航到请求的路径之外的另一个路由,在执行路由之前执行安全检查等)。的一个很好的例子是 CORS 实现。
The Party.UseRouter
Method Party.UseRouter函数
A middleware registered in this way will run under a specific Party's path prefix on all routes (whether they are matched or not, including routes that would result in an error.) All child Parties inherit them unless Party.ResetRouteFilters()
or Party.Reset()
is called. They are executed in the order they are registered, and they run before UseGlobal
and Use
on matched routes or UseError
on errors.
以这种方式注册的中间件将在以特定Party的路径为前缀的所有路由(无论它们是否匹配,包括可能导致错误的路由)上运行。所有子Party都继承它们,除非调用了 Party.ResetRouteFilters()
或 Party.Reset()。
它们按照注册的顺序执行,并且在UseGlobal
和与路由匹配的Use
或出错时的 UseEror
之前运行。
The Application.UseGlobal
Method Application.UseGlobal函数
A middleware registered in this way will run on all previous and future routes, and registered error handlers (pages matched with OnErrorCode
), in the whole Application (all Parties and Subdomains included.) They are also executed in the order they are registered, and they run before Use
or UseError
.
以这种方式注册的中间件将在整个应用程序(包括所有Party和子域)的所有先前和未来路由上运行,并注册错误处理程序(与 OnErrorCode
匹配的页面)。它们也按照注册的顺序执行,并且在Use或UseError
之前运行。
The Party.Use
Method Party.Use函数
A middleware registered in this way will run after UseGlobal
under a particular Party and its children. They are also executed in the order they are registered, and they run right before the Handle itself (such as Get
, Post
, Patch
, ...) It is not executed on errors.
以这种方式注册的中间件将在特定Pary及其子Party下的UseGlobal
之后运行。它们也按照注册的顺序执行,并且它们在句柄本身之前运行(例如Get,Post,Patch,... )它不会在出错时执行。
The Party.UseError
Method Party.UseError函数
A middleware registered in this way will run only when an HTTP error is encountered (such as the case with an unavailable 404
or protected 401
resources), unlike Use
. They fire for all children Parties and will be called in the order they were registered.
以这种方式注册的中间件仅在遇到 HTTP 错误时才会运行(例如,404
不可用或401受保护的资源的情况),这与 Use
不同。他们为所有子Party使用,并将按照他们登记的顺序调用。
The Party.Done
Method Party.Done函数
Register handler to run after routes middleware and handlers under a specific Party and its children. Requires a ctx.Next
call on the last route's handler, unless Execution Rules are modified.
注册处理程序以在路由中间件和特定Party和它的子Party下的处理程序之后运行。最后一个路由的处理程序需要调用ctx.Next,除非执行规则被修改。
The Application.DoneGlobal
Method Application.DoneGlobal函数
UseGlobal
but for the Done
handlers. Runs on the Application instance level, after everything else.UseGlobal
相同,但适用于 Done
处理程序。在应用程序实例级别上运行,在所有其他操作之后运行。func routerWrapper(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) { if r.URL.Path == "/" { w.Write([]byte("#1 .WrapRouter\n")) /* Note for new Gophers: If we Write anything here on an error resource in the raw `net/http` wrapper like this one, then the response writer will automatically send a `200` OK status code (when we first write). Any error handler executed after this will not fire as expected. Also, when `w.WriteHeader` is called you can NOT change the status code later on. In Iris Handlers, if you write before the status code has been set, then it will also automatically send the 200 OK status code which then cannot be changed later. However, if we call `ctx.StatusCode` inside an Iris Handler without writing any content, then we can change the status code later on. When you need to change that behaviour, you must start the handler with a `ctx.Record` call. */ } // Continue by executing the Iris Router and let it do its job. router(w, r) } func routerMiddleware(ctx iris.Context) { if ctx.Path() == "/" { ctx.WriteString("#2 .UseRouter\n") // The same caveat described in routerWrapper applies here as well. } ctx.Next() } func globalMiddleware(ctx iris.Context) { ctx.WriteString("#3 .UseGlobal\n") ctx.Next() } func useMiddleware(ctx iris.Context) { ctx.WriteString("#4 .Use\n") ctx.Next() } func errorMiddleware(ctx iris.Context) { ctx.WriteString("#3 .UseError\n") ctx.Next() }
Run our simple application:
$ go run main.go Now listening on: http://localhost:8080 Application started. Press CTRL+C to shut down.
Point your browser to http://localhost:8080
, and the output should look exactly like this:
#1 .WrapRouter #2 .UseRouter #3 .UseGlobal #4 .Use Main Handler
And for a page that should give a 404, such as http://localhost:8080/a_not_found_resource
:
#3 .UseGlobal #3 .UseError 404 Error Handler
(Note that WrapRouter
and UseRouter
are still fired 1st and 2nd, just not shown in the output. Read the block comment to learn why.)
OnErrorCode
:OnErrorCode
registered, your UseGlobal
will not be executed.#3 .UseError Not Found
Modify A Middleware
UseRouter
to run everywhere except on the static.example.com
subdomain.package main import ( "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/middleware/basicauth" ) func main() { users := map[string]string{"username":"password"} auth := basicauth.Default(users) app.UseRouter(skipStaticSubdomain(auth)) // <-- // [...] app.Listen(":80") } func skipStaticSubdomain(handler iris.Handler) iris.Handler { return func(ctx iris.Context) { if ctx.Subdomain() == "static." { // continue to the next or main handler and exit. ctx.Next() return } handler(ctx) } }
And the second way is by using the iris.NewConditionalHandler:
type Filter func(Context) bool
func NewConditionalHandler(filter Filter, handlers ...Handler) Handler
Here is a usage example, which checks if a subdomain exists and it is NOT the static.
one:
app.UseRouter(iris.NewConditionalHandler(isNotStaticSubdomain, auth))
func isNotStaticSubdomain(ctx iris.Context) bool { return ctx.Subdomain() != "static." }
That's all. When you've got the idea, it's trivial.
Transfer Data Between Handlers
iris.Context
. This instance is shared across the handlers chain—the Context.Values()
returns temporary memory storage which can be used to transfer data between middleware and handlers.Set(key string, value interface{}) (Entry, bool) Get(key string) interface{}
Usage
Set a value:
func myMiddleware(ctx iris.Context) { ctx.Values().Set("key", value) }
Get a value:
func myHandler(ctx iris.Context) { value := ctx.Values().Get("key") }
Complete list of the memstore methods.
Writing a middleware
package main import "github.com/kataras/iris/v12" func main() { app := iris.New() // or app.Use(before) and app.Done(after). app.Get("/", before, mainHandler, after) app.Listen(":8080") } func before(ctx iris.Context) { shareInformation := "this is a sharable information between handlers" requestPath := ctx.Path() println("Before the mainHandler: " + requestPath) ctx.Values().Set("info", shareInformation) ctx.Next() // execute the next handler, in this case the main one. } func after(ctx iris.Context) { println("After the mainHandler") } func mainHandler(ctx iris.Context) { println("Inside mainHandler") // take the info from the "before" handler. info := ctx.Values().GetString("info") // write something to the client as a response. ctx.HTML("<h1>Response</h1>") ctx.HTML("<br/> Info: " + info) ctx.Next() // execute the "after". }
$ go run main.go # and navigate to the http://localhost:8080 Now listening on: http://localhost:8080 Application started. Press CTRL+C to shut down. Before the mainHandler: / Inside mainHandler After the mainHandler
Removing a Handler from a Route
RemoveHandler
method of the *Route
registered by the Handle/Get/Post/Put...
methods. The RemoveHandler
method expects the handler name (it's the PC func, see HandlerName
of the iris context
subpackage) or the handler itself.func middleware(ctx iris.Context) {
// [...]
}
func main() {
app := iris.New()
// Register the middleware to all matched routes.
app.Use(middleware)
// Handlers = middleware, other
app.Get("/", index)
// Handlers = otherConvert http.Handler/HandlerFunc
app.Get("/other", other).RemoveHandler(middleware)
}
Execution Rules
You could also use the ExecutionRules
to force Begin(UseXXX) and Finish(DoneXXX) handlers to be executed without the requirement of a ctx.Next()
call, to forcibly forward them:
app.SetExecutionRules(iris.ExecutionRules{ // Begin: ... // Main: ... Done: iris.ExecutionOptions{Force: true}, })
Convert http.Handler/HandlerFunc
net/http
package which is modernized by the Go Authors on each new release of the Go Programming Language.net/http
is compatible with Iris using the iris.FromStd(aThirdPartyMiddleware)
. Remember, ctx.ResponseWriter()
and ctx.Request()
returns the same net/http
input arguments of an http.Handler.Built-in
Middleware |
Example |
basic authentication |
iris/_examples/auth/basicauth |
hCaptcha |
iris/_examples/auth/recaptcha |
jwt |
iris/_examples/auth/jwt |
request logger |
iris/_examples/logging/request-logger |
HTTP method override |
iris/middleware/methodoverride/methodoverride_test.go |
profiling (pprof) |
iris/_examples/pprof |
Google reCAPTCHA |
iris/_examples/auth/recaptcha |
rate |
iris/_examples/request-ratelimit |
recovery |
iris/_examples/recover |
requestid |
iris/middleware/requestid/requestid_test.go |
rewrite |
iris/_examples/routing/rewrite |
Community
Middleware | Description | Example |
casbin | An authorization library that supports access control models like ACL, RBAC, ABAC | iris-contrib/middleware/casbin/_examples |
cloudwatch | AWS cloudwatch metrics middleware | iris-contrib/middleware/cloudwatch/_example |
cors | HTTP Access Control | iris-contrib/middleware/cors/_example |
csrf | Cross-Site Request Forgery Protection | iris-contrib/middleware/csrf/_example |
jwt | Middleware checks for a JWT on the Authorization header on incoming requests and decodes it | iris-contrib/middleware/jwt/_example |
new relic | Official New Relic Go Agent | iris-contrib/middleware/newrelic/_example |
prometheus | Easily create metrics endpoint for the prometheus instrumentation tool | iris-contrib/middleware/prometheus/_example |
secure | Middleware that implements a few quick security wins | iris-contrib/middleware/secure/_example |
sentry-go (ex. raven) | Sentry client in Go | sentry-go/example/iriso |
throttler | Rate limiting access to HTTP endpoints | iris-contrib/middleware/throttler/_example |
tollbooth | Generic middleware to rate-limit HTTP requests | iris-contrib/middleware/tollboothic/_examples/limit-handler |
Third-Party
func(ctx iris.Context)
, but it's also compatible with all net/http
middleware forms. See here.Middleware | Description |
csp | Content Security Policy (CSP) support |
delay | Add delays/latency to endpoints. Useful when testing effects of high latency |
digits | Middleware that handles Twitter Digits authentication |
goth | OAuth, OAuth2 authentication. Example |
permissions2 | Cookies, users and permissions. Example |
onthefly | Generate TinySVG, HTML and CSS on the fly |
RestGate | Secure authentication for REST API endpoints |
stats | Store information about your web application (response time, etc.) |
VanGoH | Configurable AWS-Style HMAC authentication middleware |
