Laravel Response 响应客户端

本篇文章逻辑较长,只说明和响应生命周期相关的必要代码。

本文主要内容顺序为:

1、执行上文管道中的then方法指定的闭包,路由的分发

2、在路由器中(Router类)找到请求($request 也就是经过全局中间件处理的请求)匹配的路由规则

3、说明路由规则的加载(会跳转到框架的boot过程),注意这部分是在处理请求之前完成的,因为一旦当我们开始处理请求,就意味着所有的路由都应该已经加载好了,供我们的请求进行匹配

4、执行请求匹配到的路由逻辑

5、生成响应,并发送给客户端

6、最后生命周期的结束

7、基本响应类的使用

前文说道,如果一个请求顺利通过了全局中间件那么就会调用管道then方法中传入的闭包

protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request'); $this->bootstrap(); // 代码如下
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
// 此方法将当前请求挂载到容器,然后执行路由器的分发
->then($this->dispatchToRouter());
} protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}

查看Illuminate\Routing\Router::dispatch方法

public function dispatch(Request $request)
{
$this->currentRequest = $request;
// 将请求分发到路由
// 跳转到dispatchToRoute方法
return $this->dispatchToRoute($request);
} public function dispatchToRoute(Request $request)
{
// 先跳转到findRoute方法
return $this->runRoute($request, $this->findRoute($request));
} // 见名之意 通过给定的$request 找到匹配的路由
protected function findRoute($request)
{
// 跳转到Illuminate\Routing\RouteCollection::match方法
$this->current = $route = $this->routes->match($request);
$this->container->instance(Route::class, $route);
return $route;
}

查看Illuminate\Routing\RouteCollection::match方法

/**
* Find the first route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function match(Request $request)
{
// 根据请求动作找到全局匹配的路由
// 可以自行打印下$routes
$routes = $this->get($request->getMethod());
// 匹配路由 下面查看框架如何生成的路由规则!!!
$route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) {
return $route->bind($request);
} $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) {
return $this->getRouteForMethods($request, $others);
} throw new NotFoundHttpException;
}

下面说明框架如何加载的路由规则

Application::boot方法

// 主要逻辑是调用服务提供者的boot方法
array_walk($this->serviceProviders, function ($p) {
$this->bootProvider($p);
});

App\Providers\RouteServiceProvider::boot方法

public function boot()
{
// 调用父类Illuminate\Foundation\Support\Providers\RouteServiceProvider的boot方法
parent::boot();
}

Illuminate\Foundation\Support\Providers\RouteServiceProvider::boot方法

public function boot()
{
$this->setRootControllerNamespace(); if ($this->routesAreCached()) {
$this->loadCachedRoutes();
} else {
// 就看这个loadRoutes方法
$this->loadRoutes(); $this->app->booted(function () {
// dd(get_class($this->app['router']));
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
} /**
* Load the application routes.
* 看注释就知道我们来对了地方
* @return void
*/
protected function loadRoutes()
{
// 调用App\Providers\RouteServiceProvider的map方法
if (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}

App\Providers\RouteServiceProvider::map方法

public function map()
{
// 为了调试方便我注释掉了api路由
// $this->mapApiRoutes(); // 这两个都是加载路由文件 这里查看web.php
$this->mapWebRoutes();
} protected function mapWebRoutes()
{
// 调用Router的__call方法 返回的是RouteRegistrar实例
Route::middleware('web')
->namespace($this->namespace)
// 调用RouteRegistrar的namespace方法 触发__call魔术方法 // 依然是挂载属性 可自行打印
// Illuminate\Routing\RouteRegistrar {#239 ▼
// #router: Illuminate\Routing\Router {#34 }
// #attributes: array:2 [▼
// "middleware" => array:1 [▼
// 0 => "web"
// ]
// "namespace" => "App\Http\Controllers"
// ]
// #passthru: array:7 []
// #allowedAttributes: array:7 []
// #aliases: array:1 []
// } // 调用RouteRegistrar的group方法
->group(base_path('routes/web.php'));
}

Router::__call方法

public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
} if ($method === 'middleware') {
// 调用了RouteRegistrar的attribute方法 只是挂载路由属性
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
} return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
}

Illuminate\Routing\RouteRegistrar::__call方法

public function __call($method, $parameters)
{
if (in_array($method, $this->passthru)) {
// 当使用get post等方法的时候
return $this->registerRoute($method, ...$parameters);
} if (in_array($method, $this->allowedAttributes)) {
if ($method === 'middleware') {
return $this->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
// dd($method); // namespace
return $this->attribute($method, $parameters[0]);
} throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}

Illuminate\Routing\RouteRegistrar::group方法

public function group($callback)
{
// dd($this->attributes, $callback);
// array:2 [▼
// "middleware" => array:1 [▼
// 0 => "web"
// ]
// "namespace" => "App\Http\Controllers"
// ]
// "/home/vagrant/code/test1/routes/web.php" // 查看Router的group方法
$this->router->group($this->attributes, $callback);
}

Router::group方法

public function group(array $attributes, $routes)
{
$this->updateGroupStack($attributes); // 查看loadRoutes方法 /home/vagrant/code/test1/routes/web.php
$this->loadRoutes($routes); array_pop($this->groupStack);
} protected function loadRoutes($routes)
{
if ($routes instanceof Closure) {
// 用于闭包嵌套 laravel的路由是可以随意潜逃组合的
$routes($this);
} else {
// 加载路由文件 /home/vagrant/code/test1/routes/web.php
(new RouteFileRegistrar($this))->register($routes);
}
}

Illuminate\Routing\RouteFileRegistrar 文件

protected $router;

public function __construct(Router $router)
{
$this->router = $router;
} public function register($routes)
{
$router = $this->router;
// 终于加载到了路由文件
// require("/home/vagrant/code/test1/routes/web.php");
// 看到这里就到了大家熟悉的Route::get()等方法了
// 道友们可能已经有了有趣的想法: 可以在web.php等路由文件中继续require其他文件
// 便可实现不同功能模块的路由管理
require $routes;
}

了解了理由加载流程,下面举个简单例子,laravel如何注册一个路由

// web.php中
Route::get('routecontroller', "\App\Http\Controllers\Debug\TestController@index"); // 跳转到Router的get方法
/**
* Register a new GET route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function get($uri, $action = null)
{
// dump($uri, $action);
// $uri = routecontroller
// $action = \App\Http\Controllers\Debug\TestController@index
// 跳转到addRoute方法
return $this->addRoute(['GET', 'HEAD'], $uri, $action);
} /**
* Add a route to the underlying route collection.
*
* @param array|string $methods
* @param string $uri
* @param \Closure|array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
// ['GET', 'HEAD'], $uri, $action
public function addRoute($methods, $uri, $action)
{
// routes是routecollection实例
// 跳转到createRoute方法
// 跳转到RouteCollection的add方法
return $this->routes->add($this->createRoute($methods, $uri, $action));
} /**
* Create a new route instance.
*
* @param array|string $methods
* @param string $uri
* @param mixed $action
* @return \Illuminate\Routing\Route
*/
// ['GET', 'HEAD'], $uri, $action
protected function createRoute($methods, $uri, $action)
{
// 跳转到actionReferencesController方法
if ($this->actionReferencesController($action)) {
$action = $this->convertToControllerAction($action);
// dump($action);
// array:2 [▼
// "uses" => "\App\Http\Controllers\Debug\TestController@index"
// "controller" => "\App\Http\Controllers\Debug\TestController@index"
// ]
} // 创建一个对应路由规则的Route实例 并且添加到routes(collection)中
// 返回到上面的addRoute方法
// 请自行查看Route的构造方法
$route = $this->newRoute(
// dump($this->prefix);
// routecontroller
$methods, $this->prefix($uri), $action
); if ($this->hasGroupStack()) {
$this->mergeGroupAttributesIntoRoute($route);
} $this->addWhereClausesToRoute($route); return $route;
} /**
* Determine if the action is routing to a controller.
*
* @param array $action
* @return bool
*/
// 判断是否路由到一个控制器
protected function actionReferencesController($action)
{
// 在此例子中Route::get方法传递的是一个字符串
if (! $action instanceof Closure) {
// 返回true
return is_string($action) || (isset($action['uses']) && is_string($action['uses']));
} return false;
}

RouteCollection的add方法

/**
* Add a Route instance to the collection.
*
* @param \Illuminate\Routing\Route $route
* @return \Illuminate\Routing\Route
*/
public function add(Route $route)
{
// 跳转吧
$this->addToCollections($route); $this->addLookups($route); // 最终一路返回到Router的get方法 所以我们可以直接打印web.php定义的路由规则
return $route;
} /**
* Add the given route to the arrays of routes.
*
* @param \Illuminate\Routing\Route $route
* @return void
*/
protected function addToCollections($route)
{
$domainAndUri = $route->getDomain().$route->uri();
// dump($route->getDomain(), $route->uri()); null routecontroller
foreach ($route->methods() as $method) {
// 将路由规则挂载到数组 方便匹配
$this->routes[$method][$domainAndUri] = $route;
}
// 将路由规则挂载的数组 方便匹配
$this->allRoutes[$method.$domainAndUri] = $route;
}

至此就生成了一条路由 注意我这里将注册api路由进行了注释,并且保证web.php中只有一条路由规则

以上是路由的加载 这部分是在$this->bootstrap()方法中完成的,还远没有到达路由分发和匹配的阶段,希望大家能够理解,至此路由规则生成完毕 保存到了RouteCollection实例中,每个路由规则都是一个Route对象,供请求进行匹配

下面根据此条路由进行匹配,并执行返回结果

我们回到Illuminate\Routing\RouteCollection::match方法

public function match(Request $request)
{
// 获取符合当前请求动作的所有路由
// 是一个Route对象数组 每一个对象对应一个route规则
$routes = $this->get($request->getMethod()); // 匹配到当前请求路由
$route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) {
// 将绑定了请求的Route实例返回
return $route->bind($request);
} $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) {
return $this->getRouteForMethods($request, $others);
} throw new NotFoundHttpException;
} // 该方法中大量使用了collect方法 请查看laravel手册
protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
{
// dump(get_class_methods(get_class(collect($routes))));
// dump(collect($routes)->all()); // items数组 protected属性
// dump(collect($routes)->items); // items属性是一个数组 // 当注册一个兜底路由的时候 (通过Route::fallback方法)对应$route的isFallback会被设为true // partition方法根据传入的闭包将集合分成两部分
// 具体实现可以查看手册 集合部分
[$fallbacks, $routes] = collect($routes)->partition(function ($route) {
return $route->isFallback;
}); // 将兜底路由放到集合后面 并且通过first方法找到第一个匹配的路由
return $routes->merge($fallbacks)->first(function ($value) use ($request, $includingMethod) {
return $value->matches($request, $includingMethod);
});
}

Router文件

protected function findRoute($request)
{
// 可以打印$route 你会发现和你在web.php中打印的是同一个Route对象
$this->current = $route = $this->routes->match($request);
// 将匹配到的路由实例挂载到容器
$this->container->instance(Route::class, $route); return $route;
} public function dispatchToRoute(Request $request)
{
// 跳转到runRoute方法
return $this->runRoute($request, $this->findRoute($request));
} protected function runRoute(Request $request, Route $route)
{
// 给request帮顶当前的route 可以使用$request->route()方法 获取route实例
// 你也可以随时在你的业务代码中通过容器获得当前Route实例
// app(Illuminate\Routing\Route::class)
$request->setRouteResolver(function () use ($route) {
return $route;
}); $this->events->dispatch(new RouteMatched($route, $request)); // 开始准备响应了
return $this->prepareResponse($request,
// 跳转到runRouteWithinStack方法
$this->runRouteWithinStack($route, $request)
);
} protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); // 依旧是一个pipeline 我们跳转到$route->run方法
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse( $request, $route->run()
);
});
}

Route::run方法 注意此方法的返回值是直接从匹配的控制器或者闭包中返回的

public function run()
{
$this->container = $this->container ?: new Container; try {
// 如果是一个控制器路由规则
// 显然我们的此条路由是一个控制器路由
if ($this->isControllerAction()) {
// 将执行的结果返回给$route->run()
// 跳回到上面的prepareResponse方法
return $this->runController();
} // 如果是一个闭包路由规则ControllerDispatcher
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
} /**
* Run the route action and return the response.
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController()
{
//
return $this->controllerDispatcher()->dispatch(
$this,
// 通过容器解析当前路由控制器实例
$this->getController(),
// 获取当前路由控制器方法
$this->getControllerMethod()
);
}

Illuminate\Routing\ControllerDispatcher::dispatch方法

/**
* Dispatch a request to a given controller and method.
*
* @param \Illuminate\Routing\Route $route
* @param mixed $controller
* @param string $method
* @return mixed
*/
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
); if (method_exists($controller, 'callAction')) {
// 执行基类控制器中的callAction方法并返回执行结果
return $controller->callAction($method, $parameters);
} return $controller->{$method}(...array_values($parameters));
}

控制器方法返回的结果到Router::runRouteWithinStack方法

protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse(
// 返回到这里 然后执行prepareResponse方法
$request, $route->run()
);
});
} // 实际调用的是toResponse方法
// 注意这里的$response是直接从控制器中返回的任何东西
public static function toResponse($request, $response)
{
if ($response instanceof Responsable) {
// 我们当然可以直接从控制器中返回一个实现了Responsable接口的实例
$response = $response->toResponse($request);
} if ($response instanceof PsrResponseInterface) {
// 什么??? laravel还支持psr7?? 当然了 后面会附上使用文档
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif ($response instanceof Model && $response->wasRecentlyCreated) {
// 知道为什么laravel允许直接返回一个模型了吗
$response = new JsonResponse($response, 201);
} elseif (! $response instanceof SymfonyResponse &&
// 知道laravel为什么允许你直接返回数组了吗
($response instanceof Arrayable ||
$response instanceof Jsonable ||
$response instanceof ArrayObject ||
$response instanceof JsonSerializable ||
is_array($response))) {
$response = new JsonResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
// 如果没匹配到 比如response是一个字符串,null等 直接生成响应类
// 我们从laravel的Response构造方法开始梳理
$response = new Response($response);
} if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
$response->setNotModified();
} return $response->prepare($request);
}

首先我们来看直接生成laravel响应 Illuminate\Http\Response

继承了Symfony\Component\HttpFoundation\Response

// Symfony\Component\HttpFoundation\Response
public function __construct($content = '', int $status = 200, array $headers = [])
{
// 可以看到基本什么都没做
$this->headers = new ResponseHeaderBag($headers);
// 调用Illuminate\Http\Response的setContent方法 设置响应内容呗
$this->setContent($content);
$this->setStatusCode($status);
$this->setProtocolVersion('1.0');
} // Illuminate\Http\Response::setContent
public function setContent($content)
{
$this->original = $content; // shouldBeJson方法将实现了特定接口的response或者是一个array的response转换为
// 并设置响应头
if ($this->shouldBeJson($content)) {
$this->header('Content-Type', 'application/json');
// morphToJson方法保证最终给此响应设置的响应内容为json串
$content = $this->morphToJson($content);
} elseif ($content instanceof Renderable) {
$content = $content->render();
} // Symfony\Component\HttpFoundation\Response 如果最终设置的响应内容不是null或者字符串或者实现了__toString方法的类 那么跑出异常, 否则设置响应内容
parent::setContent($content); return $this;
} // Symfony\Component\HttpFoundation\Response::setContent方法
public function setContent($content)
{
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
// php官方建议不要使用gettype方法获取变量的类型
throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
}
// (string) 会触发__toString方法 如何对象允许的话
$this->content = (string) $content;
return $this;
}

拿到响应后执行return $response->prepare($request);

/**
* Prepares the Response before it is sent to the client.
*
* This method tweaks the Response to ensure that it is
* compliant with RFC 2616. Most of the changes are based on
* the Request that is "associated" with this Response.
*
* @return $this
*/
// 总的来说就是设置各种响应头 注意此时并未发送响应
public function prepare(Request $request)
{
$headers = $this->headers;
// 如果是100 204 304系列的状态码 就删除响应数据 删除对应的数据头
if ($this->isInformational() || $this->isEmpty()) {
$this->setContent(null);
$headers->remove('Content-Type');
$headers->remove('Content-Length');
} else {
// Content-type based on the Request
if (!$headers->has('Content-Type')) {
$format = $request->getPreferredFormat();
if (null !== $format && $mimeType = $request->getMimeType($format)) {
$headers->set('Content-Type', $mimeType);
}
} // Fix Content-Type
$charset = $this->charset ?: 'UTF-8';
if (!$headers->has('Content-Type')) {
$headers->set('Content-Type', 'text/html; charset='.$charset);
} elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
// add the charset
$headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
} // Fix Content-Length
if ($headers->has('Transfer-Encoding')) {
$headers->remove('Content-Length');
} if ($request->isMethod('HEAD')) {
// cf. RFC2616 14.13
$length = $headers->get('Content-Length');
$this->setContent(null);
if ($length) {
$headers->set('Content-Length', $length);
}
}
} // Fix protocol
if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
$this->setProtocolVersion('1.1');
} // Check if we need to send extra expire info headers
if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
$headers->set('pragma', 'no-cache');
$headers->set('expires', -1);
} $this->ensureIEOverSSLCompatibility($request); if ($request->isSecure()) {
foreach ($headers->getCookies() as $cookie) {
$cookie->setSecureDefault(true);
}
} return $this;
} // 至此我们的响应封装好了 等待发送给客户端
// 在发送之前 还要将响应逐步返回
// 值得注意的是 如果你给此路由设置了后置中间件 可能如下
public function handle($request, Closure $next)
{
// 此时拿到的$response就是我们上面响应好了一切 准备发送的响应了 希望你能理解后置中间件的作用了
$response = $next($request);
// header方法位于ResponseTrait
$response->header('Server', 'xy');
return $response;
}

拿到准备好的响应了,逐级向调用栈行层返回,关系如下

响应返回到Router::runRoute方法
再返回到Router::dispatchToRoute方法
再返回到Router::dispatch方法
再返回到Illuminate\Foundation\Http::sendRequestThroughRouter方法 (注意只要是通过了管道都要注意中间件的类型)
最终返回到index.php中 $response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
); $response->send(); $kernel->terminate($request, $response);

我们来看send方法 Symfony\Component\HttpFoundation\Response::send

public function send()
{
// 先发送响应头
$this->sendHeaders();
// 再发送响应主体
$this->sendContent(); if (\function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
static::closeOutputBuffers(0, true);
} return $this;
} public function sendHeaders()
{
// headers have already been sent by the developer
if (headers_sent()) {
return $this;
} // headers
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
$replace = 0 === strcasecmp($name, 'Content-Type');
foreach ($values as $value) {
// 将之前设置的各种头发送出去
header($name.': '.$value, $replace, $this->statusCode);
}
} // cookies
foreach ($this->headers->getCookies() as $cookie) {
// 告诉客户端要设置的cookie
header('Set-Cookie: '.$cookie, false, $this->statusCode);
} // status
// 最后发送个status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); return $this;
} // 发送响应内容
public function sendContent()
{
// 想笑吗 就是这么简单
echo $this->content; return $this;
}
// 至此真的响应了客户端了

$kernel->terminate($request, $response);

Illuminate\Foundation\Http\Kernel::terminate方法

/**
* Call the terminate method on any terminable middleware.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
public function terminate($request, $response)
{
// 调用实现了terminate方法的中间件
$this->terminateMiddleware($request, $response);
// 执行注册的callback
$this->app->terminate();
}

laravel将控制器(闭包)返回的数据封装成response对象

public static function toResponse($request, $response)
{
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
} if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif ($response instanceof Model && $response->wasRecentlyCreated) {
$response = new JsonResponse($response, 201);
} elseif (! $response instanceof SymfonyResponse &&
($response instanceof Arrayable ||
$response instanceof Jsonable ||
$response instanceof ArrayObject ||
$response instanceof JsonSerializable ||
is_array($response))) {
$response = new JsonResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response);
} if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
$response->setNotModified();
} return $response->prepare($request);
}

观察上面的代码发现:

上面代码的作用是将路由节点返回的数据封装成Response对象等待发送

并且上面的代码存在大量的instanceof判断 (为什么要这样呢 是因为一旦我们从控制器中返回一个实现了

laravel指定接口的实例,laravel就知道该如何渲染这些响应给客户端 此时你可能还不清楚,请看下面的例子)

而且没有else分支(当我们直接返回Resposne实例的时候会直接走到)

并且最终都调用的都是Symfony Response的prepare方法

我们先来看Responsable接口 在laravel中任何一个实现了此接口的对象 都可以响应给客户端

<?php

namespace Illuminate\Contracts\Support;

interface Responsable
{
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
// 接收$request参数
// 返回Response对象
public function toResponse($request);
} // 下面我们在控制器中返回一个实现此接口的实例
// 要实现的逻辑: 接收一个订单id 根据订单状态生成不同的响应,返回给客户端 1 定义路由
Route::get('yylh/{order}', "\App\Http\Controllers\Debug\TestController@checkStatus"); 2 创建响应
namespace App\Responses; use App\Models\Order;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse; class OrderStatusRes implements Responsable
{
protected $status; public function __construct(Order $order)
{
$this->status = $order->status;
} public function toResponse($request)
{
if ($this->status) {
// 订单以完成
return new JsonResponse('order completed', 200);
}
// 订单未结算
return view('needToCharge');
}
} 3 创建控制器
<?php namespace App\Http\Controllers\Debug; use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Responses\OrderStatusRes; class TestController extends Controller
{
public function checkStatus(Order $order)
{
return new OrderStatusRes($order);
}
} // 进行访问测试
// http://homestead.test/yylh/1
// http://homestead.test/yylh/2
// 可以看到丧心病狂的我们 通过控制器中的一行代码 就实现了根据订单的不同状态回复了不同的响应
// 我想说什么你们应该已经知道了

看toResponse代码 我们发现 只要我们想办法返回符合laravel规定的数据,最终都会被转换成laravel response实例 比如我们可以返回Responsable实例,Arrayable实例,Jsonable实例等等,大家可以尝试直接返回return new Response(),Response::create等等

Route::get('rawReponse', function () {

​ return new Response(range(1,10));

});

更多请查看这位老哥的博客

通过十篇水文,分享了从类的自动加载,到走完laravel的生命周期。

第一次写博客不足太多,不爱看大量代码的道友,可以查看这位外国老哥的博客,其代码较少,但逻辑清晰明了。发现错误,欢迎指导,感谢!!!

collection文档

laravel中使用psr7

Laravel Reponse 响应客户端的更多相关文章

  1. 跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

    上次我们通过问题"启动服务器,程序都干了什么?",跟着源码,深入了解了 Redis 服务器的启动过程. 既然启动了 Redis 服务器,那我们就要连上 Redis 服务干些事情.这 ...

  2. 跟着大彬读源码 - Redis 3 - 服务器如何响应客户端请求?(下)

    继续我们上一节的讨论.服务器启动了,客户端也发送命令了.接下来,就要到服务器"表演"的时刻了. 1 服务器处理 服务器读取到命令请求后,会进行一系列的处理. 1.1 读取命令请求 ...

  3. MVC WebAPI中响应客户端请求返回图片

    // GET api/values public HttpResponseMessage Get() {     Image img = GetImage();     MemoryStream ms ...

  4. laravel常用响应操作

  5. Laravel学习:请求到响应的生命周期

    Laravel请求到响应的整个执行过程,主要可以归纳为四个阶段,即程序启动准备阶段.请求实例化阶段.请求处理阶段.响应发送和程序终止阶段. 程序启动准备阶段 服务容器实例化 服务容器的实例化和基本注册 ...

  6. 利用Socket 实现多客户端的请求与响应

    import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Serve ...

  7. JSP基础知识➣客户端请求与服务端响应(三)

    JSP客户端请求 浏览器请求服务器端,信息头的一些重要内容,在以后的网络编程中将会经常见到这些信息: Accept:指定浏览器或其他客户端可以处理的MIME类型.它的值通常为 image/png 或 ...

  8. laravel基础课程---8、laravel响应和视图(响应是什么)

    laravel基础课程---8.laravel响应和视图(响应是什么) 一.总结 一句话总结: 就是向请求返回的响应数据(一般为html(视图),当然也可以是变量值):所有的路由及控制器必须返回某个类 ...

  9. [laravel]要点

    1. routing 2. Ioc 3. Facade 4. Artisan 1.routing 参考:http://laravel.com/docs/4.2/routing 了解routing之前先 ...

随机推荐

  1. 5.15 牛客挑战赛40 B 小V的序列 关于随机均摊分析 二进制

    LINK:小V的序列 考试的时候 没想到正解 于是自闭. 题意很简单 就是 给出一个序列a 每次询问一个x 问序列中是否存在y 使得x^y的二进制位位1的个数<=3. 容易想到 暴力枚举. 第一 ...

  2. Jenkins总结3-shell脚本

    我写shell脚本的功力还很初级,基本都是现学现卖,写得不是很健壮,只能提供个思路,请大家包涵. 我使用的系统只能发函数放到shell最前面.本人还是比较推崇函数式脚本的,方便复用,目前只简单的封装了 ...

  3. 利用WxJava实现PC网站集成微信登录功能

    原文地址:https://mp.weixin.qq.com/s/rT0xL9uAdHdZck_F8nyncg 来源:微信公众号:java碎碎念 1. 微信开放平台操作步骤 微信开放平台地址:https ...

  4. 消息队列和事件循环(Event Loop)

    产生原因 为什么会有消息队列和事件循环呢?首先最关键的一点在于JS是个单线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事 ...

  5. 使用HttpClient 发送 GET、POST(FormData、Raw)、PUT、Delete请求及文件上传

    httpclient4.3.6 package org.caeit.cloud.dev.util; import java.io.File; import java.io.IOException; i ...

  6. Mixed Precision Training —— caffe-float16

    简介 最近有了突如其来的想法,如何把caffe的变得更小更快.后来翻到Nvidia开发caffe-float16,同时也看到它的论文.看完大致了解一番后,就做一下记录. 该工作的目标是,减少网络的所需 ...

  7. 未来云原生世界的“领头羊”:容器批量计算项目Volcano 1.0版本发布

    在刚刚结束的CLOUD NATIVE+ OPEN SOURCE Virtual Summit China 2020上,由华为云云原生团队主导的容器批量计算项目Volcano正式发布1.0版本,标志着V ...

  8. nvcc fatal : Path to libdevice library not specified

    安装完成后,配置环境变量,在home下的.bashrc中加入 export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH export ...

  9. JDK 1.8 中文 API CHM

    链接: https://pan.baidu.com/s/1AiJn6RM1KoEL1n_96qoQhQ 提取码: n2ya

  10. k8s使用需认证的私服仓库

    本文内容 在K8s中使用需认证的私服仓库需要导入认证信息到集群中,常规导入方式有两种: 使用Docker已登录的仓库密文导入 使用命令行创建Secret对象导入 本文介绍的就是以上两种方法. 使用Do ...