Middleware
Creating Middleware
A middleware is a piece of code that is run before or after a request is handled. It might modify the Request
or Response
.
Create a middleware by conforming to the Middleware
protocol. It has a single function intercept
which takes a Request
and next
closure. It returns an EventLoopFuture<Response>
.
Accessing the Request
If you’d like to do something with the Request
before it is handled, you can do so before calling next
. Be sure to call and return next
when you’re finished!
/// Logs all requests that come through this middleware.
struct LogRequestMiddleware: Middleware {
func intercept(_ request: Request, next: @escaping Next) -> EventLoopFuture<Response> {
Log.info("Got a request to \(request.path).")
return next(request)
}
}
You may also do something with the request asynchronously, just be sure to continue the chain with next(req)
when you are finished.
/// Runs a database query before passing a request to a handler.
struct QueryingMiddleware: Middleware {
func intercept(_ request: Request, next: @escaping Next) -> EventLoopFuture<Response> {
return User.all()
.flatMap { users in
// Do something with `users` then continue the chain
next(request)
}
}
}
Setting Data on a Request
Sometimes you may want a Middleware
to add some data to a Request
. For example, you may want to authenticate an incoming request with a Middleware
and then add a User
to it for handlers down the chain to access.
You can set generic data on a Request
using Request.set
and then access it in subsequent Middleware
or handlers via Request.get
.
For example, you might be doing some experiments with a homegrown ExperimentConfig
type. You’d like to assign random configurations of that type on a per-request basis. You might do so with a Middleware
:
struct ExperimentMiddleware: Middleware {
func intercept(_ request: Request, next: @escaping Next) -> EventLoopFuture<Response> {
let config: ExperimentConfig = ... // load a random experiment config
return next(request.set(config))
}
}
You would then intercept requests with that Middleware
and utilize the set ExperimentConfig
in your handlers.
app
.use(ExperimentalMiddleware())
.get("/experimental_endpoint") { request in
// .get() will throw an error if a value with that type hasn't been `set()` on the `Request`.
let config: ExperimentConfig = try request.get()
if config.shouldUseLoudCopy {
return "HELLO WORLD!!!!!"
} else {
return "hey, world."
}
}
Accessing the Response
If you’d like to do something with the Response
of the handled request, you can plug into the future returned by next
.
/// Logs all responses that come through this middleware.
struct LogResponseMiddleware: Middleware {
func intercept(_ request: Request, next: @escaping Next) -> EventLoopFuture<Response> {
return next(request)
// Use `flatMap` if you want to do something asynchronously.
.map { response in
Log.info("Got a response \(response.status) from \(request.path).")
return response
}
}
}
Adding Middleware to Your Application
There are a few ways to have a Middleware
intercept requests.
Global Intercepting
If you’d like a middleware to intercept all requests on an Application
, you can add it via Application.useAll
.
struct ExampleApp: Application {
func boot() {
self
.useAll(LoggingMiddleware())
// LoggingMiddleware will intercept all of these, as well as any unhandled requests.
.get("/foo") { request in "Howdy foo!" }
.post("/bar") { request in "Howdy bar!" }
.put("/baz") { request in "Howdy baz!" }
}
}
Specific Intercepting
A Middleware
can be setup to only intercept requests to specific handlers via the .use(_ middleware: Middleware)
function on an Application
. The Middleware
will intercept all requests to the subsequently defined handlers.
app
.post("/password_reset", handler: ...)
// Because this middleware is provided after the /password_reset endpoint,
// it will only affect subsequent routes. In this case, only requests to
// `/user` and `/todos` would be intercepted by the LoggingMiddleware.
.use(LoggingMiddleware())
.get("/user", handler: ...)
.get("/todos", handler: ...)
There is also a .group
function that takes a Middleware
. The Middleware
will only intercept requests handled by handlers defined in the closure.
app
.post("/user", handle: ...)
.group(middleware: CustomAuthMiddleware()) {
// Each of these endpoints will be protected by the
// `CustomAuthMiddleWare`...
$0.get("/todo", handler: ...)
.put("/todo", handler: ...)
.delete("/todo", handler: ...)
}
// ...but this one will not.
.post("/reset", handler: ...)