Middleware¶
Middleware
是一个中间件组件,它可以在 请求前
和 请求后
做一些额外的处理。
如何使用¶
以系统中间件 FileMiddleware
为例。FileMiddleware
的作用是在接收到 请求
后,先在 Public/
目录下查找是否有匹配的资源文件,如果存在的话就直接返回该资源文件。
只需要在 configure.swift
中配置下即可。
// Register middleware
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
比如,我们将一张测试图片(比如:sample.png
)存放到 Public
目录中,在本地服务已启动的情况下(假设所占端口为 8080
),访问 http://localhost:8080/sample.png
地址可直接显示 Public/
目录下的 sample.png
图片。
效果如下
Middleware 源码¶
Middleware
其实是一个 protocol
,内部定义了 respond
方法,源码如下:
public protocol Middleware {
/// Called with each `Request` that passes through this middleware.
/// - parameters:
/// - request: The incoming `Request`.
/// - next: Next `Responder` in the chain, potentially another middleware or the main router.
/// - returns: An asynchronous `Response`.
func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response>
}
可以在 respond
方法中做一些额外的处理。
FileMiddleware 源码分析¶
FileMiddleware
源码如下:
public final class FileMiddleware: Middleware {
/// The public directory.
/// - note: Must end with a slash.
private let publicDirectory: String
/// Creates a new `FileMiddleware`.
public init(publicDirectory: String) {
self.publicDirectory = publicDirectory.hasSuffix("/") ? publicDirectory : publicDirectory + "/"
}
/// See `Middleware`.
public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
// make a copy of the path
var path = request.url.path
// path must be relative.
while path.hasPrefix("/") {
path = String(path.dropFirst())
}
// protect against relative paths
guard !path.contains("../") else {
return request.eventLoop.makeFailedFuture(Abort(.forbidden))
}
// create absolute file path
let filePath = publicDirectory + path
// check if file exists and is not a directory
var isDir: ObjCBool = false
guard FileManager.default.fileExists(atPath: filePath, isDirectory: &isDir), !isDir.boolValue else {
return next.respond(to: request)
}
// stream the file
let res = request.fileio.streamFile(at: filePath)
return request.eventLoop.makeSucceededFuture(res)
}
}
查看 FileMiddleware
中的 respond
方法得知,通过 FileManager.default.fileExists
方法可以判断指定资源文件是否存在,如果存在的话,就直接将该资源文件返回给客户端,否则通过 next.respond(to: request)
来执行进一步的操作。
自定义 Middleware¶
我们将自定义一个 Middleware
组件,作用是对 Request
请求处理完后返回的 Response
的 Header
信息中统一添加一个 Key(比如:My-Key
)。
创建¶
创建 MyMiddleware.swift
文件,存放于 App/Middleware/
目录下。
import Vapor
public final class MyMiddleware: Middleware {
public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
// Handle request before
// ......
// Handle request
let resposneFuture = next.respond(to: request)
// Handle request after
return resposneFuture.flatMap { response in
response.headers.add(name: "My-Key", value: "Test123456")
return request.eventLoop.makeSucceededFuture(response)
}
}
}
调用 next.respond(to: request)
将会唤起路由相关的回调,并对请求进行处理。然后通过 response.headers.add(name: "My-Key", value: "Test123456")
来给的 Response
的 Header
信息中添加一组 Key-Value
数据(示例中 Key 是 My-Key
,Value 是 Test123456
)。
使用¶
在 configure.swift
文件中添加如下代码:
app.middleware.use(MyMiddleware())
此时启动服务后,MyMiddleware
中间件将立即生效,访问任意一个接口,返回的 Reponse
中的头信息中都将包含 My-Key
的信息。