Sisk

请求生命周期

本页由英文自动翻译。 阅读原文

下面通过一个 HTTP 请求的示例解释请求的完整生命周期。

  • 接收请求: 每个请求在请求本身和将要发送给客户端的响应之间创建一个 HTTP 上下文。该上下文来自 Sisk 内置的监听器,可以是 HttpListener、Kestrel 或 Cadente。
    • 外部请求验证:对请求进行 HttpServerConfiguration.RemoteRequestsAction 的验证。
      • 如果请求是外部的且属性为 Drop,则在不向客户端返回响应的情况下关闭连接,HttpServerExecutionStatus = RemoteRequestDropped。
    • 转发解析器配置:如果配置了 ForwardingResolver,它将在请求的原始主机上调用 OnResolveRequestHost 方法。
    • DNS 匹配:在解析出主机且配置了多个 ListeningHost 时,服务器会为请求查找相应的主机。
      • 如果没有匹配的 ListeningHost,服务器返回 400 Bad Request 响应给客户端,并向 HTTP 上下文返回 HttpServerExecutionStatus = DnsUnknownHost 状态。
      • 如果匹配到 ListeningHost,但其 Router 尚未初始化,服务器返回 503 Service Unavailable 响应给客户端,并向 HTTP 上下文返回 HttpServerExecutionStatus = ListeningHostNotReady 状态。
    • 路由绑定:将相应 ListeningHost 的路由器与接收到的 HTTP 服务器关联。
      • 如果路由器已经关联到另一个 HTTP 服务器,则会抛出 InvalidOperationException,因为路由器会主动使用服务器的配置资源,这在 HTTP 服务器初始化期间才会发生,而不会在创建 HTTP 上下文时发生。
    • 预定义头部:
      • 如果已配置,则在响应中预定义 X-Request-Id 头部。
      • 如果已配置,则在响应中预定义 X-Powered-By 头部。
    • 内容大小验证:仅当配置值大于零时,验证请求内容是否小于 HttpServerConfiguration.MaximumContentLength。
      • 如果请求发送的 Content-Length 大于配置的值,服务器返回 413 Payload Too Large 响应给客户端,并向 HTTP 上下文返回 HttpServerExecutionStatus = ContentTooLarge 状态。
    • OnHttpRequestOpen 事件会对所有已配置的 HTTP 服务器处理程序触发。
  • 路由操作: 服务器为接收到的请求调用路由器。
    • 如果路由器未找到匹配请求的路由:
      • 如果已配置 Router.NotFoundErrorHandler 属性,则调用该操作,并将操作的响应转发给 HTTP 客户端。
      • 如果该属性为 null,则返回默认的 404 Not Found 响应给客户端。
    • 如果路由器找到匹配的路由,但该路由的方法与请求的方法不匹配:
      • 如果已配置 Router.MethodNotAllowedErrorHandler 属性,则调用该操作,并将操作的响应转发给 HTTP 客户端。
      • 如果该属性为 null,则返回默认的 405 Method Not Allowed 响应给客户端。
    • 如果请求使用 OPTIONS 方法:
      • 仅当没有路由匹配请求方法(路由的方法未显式标记为 RouteMethod.Options)时,路由器才返回 200 Ok 响应给客户端。
    • 如果启用了 HttpServerConfiguration.ForceTrailingSlash 属性,且匹配的路由不是正则表达式,请求路径未以 / 结尾,并且请求方法为 GET:
      • 服务器返回 307 Temporary Redirect HTTP 响应,Location 头部指向相同路径并在末尾添加 /,返回给客户端。
    • OnContextBagCreated 事件会对所有已配置的 HTTP 服务器处理程序触发。
    • 执行所有全局的带有 BeforeResponse 标记的 IRequestHandler 实例。
      • 如果任意处理程序返回非 null 响应,则将该响应转发给 HTTP 客户端并关闭上下文。
      • 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
        • 如果已启用 Router.CallbackErrorHandler 属性,则调用它并将产生的响应返回给客户端。
        • 如果未定义该属性,则返回空响应给服务器,服务器会根据抛出的异常类型转发响应,通常为 500 Internal Server Error。
    • 执行路由中定义的且带有 BeforeResponse 标记的所有 IRequestHandler 实例。
      • 如果任意处理程序返回非 null 响应,则将该响应转发给 HTTP 客户端并关闭上下文。
      • 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
        • 如果已启用 Router.CallbackErrorHandler 属性,则调用它并将产生的响应返回给客户端。
        • 如果未定义该属性,则返回空响应给服务器,服务器会根据抛出的异常类型转发响应,通常为 500 Internal Server Error。
    • 调用路由器的操作并将其转换为 HTTP 响应。
      • 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
        • 如果已启用 Router.CallbackErrorHandler 属性,则调用它并将产生的响应返回给客户端。
        • 如果未定义该属性,则返回空响应给服务器,服务器会根据抛出的异常类型转发响应,通常为 500 Internal Server Error。
    • 执行所有全局的带有 AfterResponse 标记的 IRequestHandler 实例。
      • 如果任意处理程序返回非 null 响应,则该处理程序的响应替换之前的响应并立即转发给 HTTP 客户端。
      • 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
        • 如果已启用 Router.CallbackErrorHandler 属性,则调用它并将产生的响应返回给客户端。
        • 如果未定义该属性,则返回空响应给服务器,服务器会根据抛出的异常类型转发响应,通常为 500 Internal Server Error。
    • 执行路由中定义的且带有 AfterResponse 标记的所有 IRequestHandler 实例。
      • 如果任意处理程序返回非 null 响应,则该处理程序的响应替换之前的响应并立即转发给 HTTP 客户端。
      • 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
        • 如果已启用 Router.CallbackErrorHandler 属性,则调用它并将产生的响应返回给客户端。
        • 如果未定义该属性,则返回空响应给服务器,服务器会根据抛出的异常类型转发响应,通常为 500 Internal Server Error。
  • 处理响应: 当响应准备好后,服务器会为发送给客户端做准备。
    • 根据当前 ListeningHost.CrossOriginResourceSharingPolicy 的配置,在响应中定义跨域资源共享策略(CORS)头部。
    • 将响应的状态码和头部发送给客户端。
    • 将响应内容发送给客户端:
      • 如果响应内容是 ByteArrayContent 的子类,则直接将响应字节复制到响应输出流。
      • 如果不满足前述条件,则将响应序列化为流并复制到响应输出流。
    • 关闭流并丢弃响应内容。
    • 如果启用了 HttpServerConfiguration.DisposeDisposableContextValues,则会丢弃请求上下文中所有继承自 IDisposable 的对象。
    • OnHttpRequestClose 事件会对所有已配置的 HTTP 服务器处理程序触发。
    • 如果服务器抛出异常,则 OnException 事件会对所有已配置的 HTTP 服务器处理程序触发。
    • 如果路由允许访问日志且 HttpServerConfiguration.AccessLogsStream 不为 null,则会向日志输出写入一行日志。
    • 如果路由允许错误日志、出现异常且 HttpServerConfiguration.ErrorsLogsStream 不为 null,则会向错误日志输出写入一行日志。
    • 如果服务器正通过 HttpServer.WaitNext 等待请求,则释放互斥锁并使上下文对用户可用。

输入关键词以搜索文档和 API 参考。