前文讲到当收到申请后,swoft 将 swoole 原生的 Request 及 Response 对象封装成适宜 swoft 框架外部调用的 Swoft\Http\Message\Request
以及 Swoft\Http\Message\Response
.
接下来, 本章将追随办法 $this->dispatcher->dispatch($psrRequest, $psrResponse)
逐渐剖析申请到来后的框架的调度过程.
先看 Swoft\Http\Server\HttpDispatcher
的实现:
public function dispatch(...$params): void
{
/**
* @var Request $request
* @var Response $response
*/ [$request, $response] = $params;
$response = $this->configResponse($response);
/* @var RequestHandler $requestHandler */
$requestHandler = Swoft::getBean(RequestHandler::class);
try {
// 初始化中间件
$requestHandler->initialize($this->requestMiddlewares, $this->defaultMiddleware);
// 创立新的 HttpContext 并设置到 Context 中
// 在业务逻辑中获取到的 Context 就是这里设置的 HttpContext
// 前面附上此办法的源码调用
// Before request
$this->beforeRequest($request, $response);
// 触发 BEFORE_REQUEST 事件
// Trigger before handle event
Swoft::trigger(HttpServerEvent::BEFORE_REQUEST, null, $request, $response);
// 匹配路由, 将路由信息绑定在新的 Request 对象上, 返回
// Match router and handle
$request = $this->matchRouter($request);
// 调用 handle 解决申请, 实际上就是解决中间件
// 控制器的执行也是放在中间件中执行的
// Swoft\Http\Server\Middleware\DefaultMiddleware
$response = $requestHandler->handle($request);
} catch (Throwable $e) {
// 在解决申请时若发送异样会被零碎在此处捕捉
// 而后调用 HttpErrorDispatcher 去解决对应的异样
// 咱们在业务中注册的异样解决类就是在此处失去执行
/** @var HttpErrorDispatcher $errDispatcher */
$errDispatcher = Swoft::getSingleton(HttpErrorDispatcher::class);
// Handle request error
$response = $errDispatcher->run($e, $response);
}
try {
// 调用格式化解决对象来格式化失去的 response
// Format response content type
$response = $this->acceptFormatter->format($response);
// 触发 AFTER_REQUEST 事件
// Trigger after request
Swoft::trigger(HttpServerEvent::AFTER_REQUEST, null, $response);
// 返回内容给客户端
// 触发协程 COROUTINE_DEFER 和 COROUTINE_COMPLETE 事件, 前面附代码
// After request
$this->afterRequest($response);
} catch (Throwable $e) {
// 此步骤呈现谬误, 则示意未能将内容失常返回给客户端
// 须要写入 error 级别的谬误, 控制台会有 error 内容打印
// 如果是协程环境(request 周期内就是协程环境)
// 还会写入日志
Error::log('response error=%s(%d) at %s:%d', $e->getMessage(), $e->getCode(), $e->getFile(), $e->getLine());
}
}
办法应用的是 php 中的动静参数传递形式, 前文说过, 此办法以后获取的 request 及 response 变量是 Swoft\Http\Message\Request
以及 Swoft\Http\Message\Response
的实例.
beforeRequest 源码(创立 HttpContext):
private function beforeRequest(Request $request, Response $response): void
{$httpContext = HttpContext::new($request, $response);
// Add log data
if ($this->logger->isEnable()) {
$data = [
'event' => SwooleEvent::REQUEST,
'uri' => $request->getRequestTarget(),
'requestTime' => $request->getRequestTime(),];
$httpContext->setMulti($data);
}
Context::set($httpContext);
}
HttpContext::new 源码 (self::__instance() 实际上是获取的 bean 对象,bean 的注解是 @Bean(scope=Bean::PROTOTYPE)):
public static function new(Request $request, Response $response): self
{$instance = self::__instance();
$instance->request = $request;
$instance->response = $response;
return $instance;
}
路由匹配代码:
private function matchRouter(Request $request): Request
{$method = $request->getMethod();
$uriPath = $request->getUriPath();
/** @var Router $router */
$router = Swoft::getSingleton('httpRouter');
$result = $router->match($uriPath, $method);
// Save matched route data to request
$request = $request->withAttribute(Request::ROUTER_ATTRIBUTE, $result);
context()->setRequest($request);
return $request;
}
afterRequest 代码:
private function afterRequest(Response $response): void
{
// 后附代码
$response->send();
// Defer
Swoft::trigger(SwoftEvent::COROUTINE_DEFER);
// Destroy
Swoft::trigger(SwoftEvent::COROUTINE_COMPLETE);
}
send 代码:
public function send(): void
{
// 是否发送文件
// Is send file
if ($this->filePath) {
// 批改发送状态为 true
$this->sent = true;
// 写入 header
// Write Headers to co response
foreach ($this->getHeaders() as $key => $value) {$headerLine = implode(';', $value);
if ($key !== ContentType::KEY) {$this->coResponse->header($key, $headerLine);
}
}
// Do send file
$this->coResponse->header(ContentType::KEY, $this->fileType);
// 发送文件
$this->coResponse->sendfile($this->filePath);
return;
}
// 格式化返回内容, 并发送
// Prepare and send
$this->quickSend($this->prepare());
}
quickSend 代码, 此办法次要性能是将 Swoft 的 Response 对象通过申请解决实现取得的业务数据从新设置回 swoole 原生的 Response 对象, 并调用原生 Response 对象的 end 办法返回数据给客户端:
public function quickSend(Response $response = null): void
{
$response = $response ?: $this;
// 获取 swoole 原生 Response 对象
// 后续的设置和返回都是通过原生 Response 对象实现
// Ensure coResponse is right
$coResponse = $response->getCoResponse();
// 设置返回的 headers
// Write Headers to co response
foreach ($response->getHeaders() as $key => $value) {$headerLine = implode(';', $value);
if ($key === ContentType::KEY) {$headerLine .= '; charset=' . $response->getCharset();
$coResponse->header($key, $headerLine, $this->headerUcWords);
} else {$coResponse->header($key, $headerLine, $this->headerUcWords);
}
}
// 设置返回的 COOKIES
// Write cookies
foreach ($response->cookies as $n => $c) {$coResponse->cookie($n, $c['value'], $c['expires'], $c['path'], $c['domain'], $c['secure'], $c['httpOnly']);
}
// 设置返回的状态码
// Set status code
$coResponse->status($response->getStatusCode());
// 获取返回的 body
// Set body
$content = $response->getBody()->getContents();
// 调用 swoole 的 Response 对象的 end 办法, 发送数据给客户端
$coResponse->end($content);
// 批改发送状态为 true
// 此属性是在创立 Response 对象是初始化为 false 的
// Ensure sent
$this->sent = true;
}
总结:
1. 申请的业务逻辑是在零碎注册的 Swoft\Http\Server\Middleware\DefaultMiddleware 中间件中失去执行的.
2. 申请的业务逻辑是包裹在 try/catch 块中执行的, 出现异常会调用零碎和用户注册的异样解决 handler.
该 handler 会返回一个 Response 对象, 失常执行的中间件返回的 Response 对象将会被抛弃.
这也是 swoft 业务逻辑中出现异常后, 跨域中间件无奈失常设置 header 的起因.
对于此点的解决形式请参考自己之前的文章[swoft 中跨域设置的问题](https://segmentfault.com/a/1190000038411563)
3.swoft 返回数据的形式是将业务返回的 Swoft\Http\Message\Response 上携带的像 headers,cookies,body 等内容从新设置回 swoole 原生 Response 对象上, 而后调用原生 Response 对象返回业务数据.
4. 在执行完发送动作后,swoft 会触发协程的 deffer 和 finish 事件, 之后本次申请正式完结, 以后申请协程的生命周期也完结了.