Middleware是任何现代Web框架的重要组成部分。它允许你修改在客户端和服务器之间传递的请求(Request)和响应( Response)。
你可以将Middleware视为连接服务器与请求网络的应用客户端的逻辑链。
Basic
举例说明:我们来创建一个middleware,它会在每次响应时添加我们API的版本号:
final class VersionMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
let response = try next.respond(to: request)
response.headers["Version"] = "API v1.0"
return response
}
}
然后把这个middleware应用到Droplet中:
let drop = Droplet()
drop.middleware.append(VersionMiddleware())
您可以想象我们的VersionMiddleware
位于连接客户端和服务器的链中间。访问服务器的每一个请求和响应都必须经过这个middleware链。
BreakDown
来一行一行的分析一下上面的代码:
let response = try next.respond(to: request)
例子中的VersionMiddleware
并不想修改request
,所以我们立即将request
传给下一个middleware去响应,直到传给Droplet,Droplet得到请求后会将response
返回给客户端。
response.headers["Version"] = "API v1.0"
然后修改response
,添加Version信息到headers
里。
return response
response
中修改过的内容会在后面的middleware
中保留,最后传给客户端。
Request
middleware也可以修改或作用于request
:
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
guard request.cookies["token"] == "secret" else {
throw Abort.badRequest
}
return try next.respond(to: request)
}
这个middleware要求cookies中存储的“token”值是“secret”,否则请求就会终止。
Errors
Middleware是捕获应用程序中任何地方发生的异常的完美场所。当你使用middleware捕获异常时,你可以删除路由闭包中大量重复的逻辑。例子如下:
enum FooError: Error {
case fooServiceUnavailable
}
有这样一个你自定义的异常或者是某个API的异常,你必须对它进行捕获,否则会引起服务器异常。最明显的解决办法是在路由闭包中进行捕获:
drop.get("foo") { request in
let foo: Foo
do {
foo = try getFooFromService()
} catch {
throw Abort.badRequest
}
// continue with Foo object
}
这样可以捕获异常,但是如果在多个路由中都需要捕获这个异常呢?复制代码没问题,但是更高大上的办法就是在middleware
中捕获。
final class FooErrorMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
do {
return try next.respond(to: request)
} catch FooError.fooServiceUnavailable {
throw Abort.custom(
status: .badRequest,
message: "Sorry, we were unable to query the Foo service."
)
}
}
}
我们只需要将这个middleware
拼到drop
中。
drop.middleware.append(FooErrorMiddleware())
路由闭包中则简单得多了,并且不用担心代码重复的问题。
app.get("foo") { request in
let foo = try getFooFromService()
// continue with Foo object
}
有趣的是,这就是Abort类在Vapor中实现的原理。 AbortMiddleware可以捕获所有的Abort异常,并返回JSON响应数据。如果您想自定义Abort错误的显示方式,可以删除这个AbortMiddleware并添加自己的middleware。
Configration
将middleware添加到drop.middleware
数组中,是从应用开始就一直在用的最简单的添加middleware的方法。
你也可以通过configration
来对middleware进行添加、删除或更多操作。尤其是你在不同的开发环境下进行开发时这样做就很有用了。
configuration添加middleware的方法如下:
let drop = Droplet()
drop.addConfigurable(middleware: myMiddleware, name: "my-middleware")
然后在Config/droplet.json
文件内添加middleware
数组以及my-middleware
字段。
{
...
"middleware": {
"server": [
...
"my-middleware",
...
],
"client": [
...
]
},
...
}
my-middleware
出现在server
中就会添加到server
的middleware数组中,如果添加在client
中,就会添加到client
的middleware数组中。当然了,一个middleware可以添加到多个地方,也可以重复添加,并且会安添加顺序依次执行。
Extensions(高级用法)
Middleware 与request/response
的扩展和存储配合的很好:
final class PokemonMiddleware: Middleware {
let drop: Droplet
init(drop: Droplet) {
self.drop = drop
}
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
let response = try next.respond(to: request)
if let pokemon = response.pokemon {
if request.accept.prefers("html") {
response.view = try drop.view("pokemon.mustache", context: pokemon)
} else {
response.json = try pokemon.makeJSON()
}
}
return response
}
}
Response
的扩展:
extension Response {
var pokemon: Pokemon? {
get { return storage["pokemon"] as? Pokemon }
set { storage["pokemon"] = newValue }
}
}
在这个例子中,我们给Response添加了一个新的属性,让它持有一个Pokémon
对象。 如果middleware发现了某个或多个Response中持有Pokémon
对象,它将动态地检查客户端是否支持HTML。 如果客户端是Safari浏览器,并且支持HTML,它将返回一个Mustache视图。 如果客户端不支持HTML,它将返回JSON。
路由闭包中这么写:
import HTTP
drop.get("pokemon", Pokemon.self) { request, pokemon in
let response = Response()
response.pokemon = pokemon
return response
}
或者更进一步,让Pokemon
遵守ResponseRepresentable
协议:
import HTTP
extension Pokemon: ResponseRepresentable {
func makeResponse() throws -> Response {
let response = Response()
response.pokemon = self
return response
}
}
然后路由闭包中可以十分简洁,并且不用import HTTP
:
drop.get("pokemon", Pokemon.self) { request, pokemon in
return pokemon
}
Middleware是非常强大的,结合扩展,你可以向原生框架添加功能。
对于那些好奇的人来说,这就是Vapor如何在内部管理JSON的。 无论何时在闭包中返回JSON,它会给Response
设置json:JSON?
属性。 JSONMiddleware
会检测这个属性并将其JSON序列化添加到response
的正文中。
<b>总结:</b>Middleware在客户端和服务端中间链中对Request/Response进行“偷梁换柱”,能够极大的减少代码冗余、简化路由闭包。结合类的扩展等,又可以强化程序功能。