iOS.& Swift Books 服务器端迅速与蒸气

29
中间件 由Tanner Nelson撰写

在构建您的应用程序的过程中,您通常会发现有必要将自己的步骤集成到请求管道中。实现这一目标的最常见机制是使用一个或多个中间件。他们允许你做这样的事情:

  • 记录传入请求。
  • 捕获错误和显示消息。
  • 对特定路由的速率限制流量。

中间件实例位于路由器和连接到服务器的客户端之间。这允许它们在到达控制器之前查看和潜在的传入请求。中间件实例可以选择通过生成自己的响应来提早返回,或者它可以将请求转发到链中的下一个响应者。最终的响应者始终是您的路由器。当生成来自下一个响应者的响应时,中间件可以制作它认为所需的任何修改,或者选择将其转发回客户端。这意味着每个中间件实例都控制了传入请求 outgoing responses.

正如您在上图所示,应用程序 - 中间件A中的第一个中间件实例 - 首先从客户端接收传入请求。然后,第一个中间件可以选择将此请求传递到下一个中​​间件 - 中间件B - 等。

最终,一些组件生成响应,然后沿相反方向遍历中间件。请注意,这意味着第一件中间件收到响应 最后的 .

The protocol for 中间件 is fairly simple and should help you better understand the previous diagram:

public protocol Middleware {
  func respond(
    to request: Request, 
    chainingTo next: Responder
  ) -> EventLoopFuture<Response>
}

In the case of Middleware A, request is the incoming data from the client, while next is Middleware B. The asynchronous response returned by Middleware A goes directly to the client.

For Middleware B, request is the request passed on from Middleware A. next is the router. The future response returned by Middleware B goes to Middleware A.

蒸气的中间件

蒸气包括盒子中的一些中间件。本节向您介绍可用选项,以便您了解常用的中间件。

错误中间件

The most commonly used middleware in Vapor is ErrorMiddleware. It’s responsible for converting both synchronous and asynchronous Swift errors into HTTP responses. Uncaught errors cause the HTTP server to immediately close the connection and print an internal error log.

throw Abort(.badRequest, "Something's not quite right.")

文件中间件

Another common type of middleware is FileMiddleware. This middleware serves files from the 民众 应用程序目录中的文件夹。当您使用蒸气创建可能需要图像或样式表等静态文件时,这非常有用。

其他中间件

Vapor also provides a SessionsMiddleware, responsible for tracking sessions with connected clients. Other packages may provide middleware to help them integrate into your application. For example, Vapor’s Authentication package contains middleware for protecting your routes using basic passwords, simple bearer tokens, and even JWTs (JSON Web Tokens).

示例:todo api

既然您了解各种类型的中间件函数如何,您可以了解如何配置它们以及如何创建自己的自定义中间件类型。

$ swift run Run routes
+--------+--------------+
| GET    | /todos       |
+--------+--------------+
| POST   | /todos       |
+--------+--------------+
| DELETE | /todos/:todo |
+--------+--------------+

日志中间件

您创建的第一个中间件将记录传入请求。它将显示每个请求的以下信息:

open Package.swift
final class LogMiddleware: Middleware {
  // 1
  func respond(
    to req: Request, 
    chainingTo next: Responder
  ) -> EventLoopFuture<Response> {
    // 2
    req.logger.info("\(req)")
    // 3
    return next.respond(to: req)
  }
}
app.middleware.use(LogMiddleware())
curl localhost:8080/todos
[ INFO ] GET /todos HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
 [request-id: 4D528CE6-8C10-443A-A5BB-9A6F2BB3A6E7]
func respond(
  to req: Request, 
  chainingTo next: Responder
) -> EventLoopFuture<Response> {
  // 1
  let start = Date()
  return next.respond(to: req).map { res in
    // 2
    self.log(res, start: start, for: req)
    return res
  }
}

// 3
func log(_ res: Response, start: Date, for req: Request) {
  let reqInfo = "\(req.method.string) \(req.url.path)"
  let resInfo = "\(res.status.code) " + 
    "\(res.status.reasonPhrase)"
  // 4
  let time = Date()
    .timeIntervalSince(start)
    .readableMilliseconds
  // 5
  req.logger.info("\(reqInfo) -> \(resInfo) [\(time)]")
}
curl localhost:8080/todos
[ INFO ] GET /todos -> 200 OK [1.7ms] [request-id: ...]

秘密中间件

既然您已经学会了如何创建中间件并全局应用它,您将学习如何将中间件应用于特定路由。

final class SecretMiddleware: Middleware {
  // 1
  let secret: String

  init(secret: String) {
    self.secret = secret
  }

  // 2
  func respond(
    to request: Request,
    chainingTo next: Responder
  ) -> EventLoopFuture<Response> {
    // 3
    guard
      request.headers.first(name: .xSecret) == secret
    else {
      // 4
      return request.eventLoop.makeFailedFuture(
        Abort(
          .unauthorized, 
          reason: "Incorrect X-Secret header."))
    }
    // 5
    return next.respond(to: request)
  }
}
extension SecretMiddleware {
  // 1
  static func detect() throws -> Self {
    // 2
    guard let secret = Environment.get("SECRET") else {
      // 3
      throw Abort(
        .internalServerError, 
        reason: """
          No SECRET set on environment. \
          Use export SECRET=<secret>
          """)
    }
    // 4
    return .init(secret: secret)
  }
}
// 1
try app.group(SecretMiddleware.detect()) { secretGroup in
  // 2
  secretGroup.post("todos", use: todoController.create)
  secretGroup.delete(
    "todos", 
    ":id", 
    use: todoController.delete)
}
SECRET=foo

{
    "error": true,
    "reason": "Incorrect X-Secret header."
}

然后去哪儿?

中间件 is extremely useful for creating large web applications. It allows you to apply restrictions and transformations globally or to just a few routes using discrete, re-usable components. In this chapter, you learned how to create a global LogMiddleware that displayed information about all incoming requests to your app. You then created SecretMiddleware, which could protect select routes from public access.

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。