请求生命周期
本页由英文自动翻译。 阅读原文
下面通过一个 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,服务器返回 400 Bad Request 响应给客户端,并向 HTTP 上下文返回
- 路由绑定:将相应 ListeningHost 的路由器与接收到的 HTTP 服务器关联。
- 如果路由器已经关联到另一个 HTTP 服务器,则会抛出
InvalidOperationException,因为路由器会主动使用服务器的配置资源,这在 HTTP 服务器初始化期间才会发生,而不会在创建 HTTP 上下文时发生。
- 如果路由器已经关联到另一个 HTTP 服务器,则会抛出
- 预定义头部:
- 如果已配置,则在响应中预定义
X-Request-Id头部。 - 如果已配置,则在响应中预定义
X-Powered-By头部。
- 如果已配置,则在响应中预定义
- 内容大小验证:仅当配置值大于零时,验证请求内容是否小于 HttpServerConfiguration.MaximumContentLength。
- 如果请求发送的
Content-Length大于配置的值,服务器返回 413 Payload Too Large 响应给客户端,并向 HTTP 上下文返回HttpServerExecutionStatus = ContentTooLarge状态。
- 如果请求发送的
OnHttpRequestOpen事件会对所有已配置的 HTTP 服务器处理程序触发。
- 外部请求验证:对请求进行 HttpServerConfiguration.RemoteRequestsAction 的验证。
- 路由操作: 服务器为接收到的请求调用路由器。
- 如果路由器未找到匹配请求的路由:
- 如果已配置 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头部指向相同路径并在末尾添加/,返回给客户端。
- 服务器返回 307 Temporary Redirect HTTP 响应,
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。
- 如果此步骤抛出错误且 HttpServerConfiguration.ThrowExceptions 被禁用:
- 执行所有全局的带有
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 等待请求,则释放互斥锁并使上下文对用户可用。