您好,登錄后才能下訂單哦!
在Laravel使用Reponse實現一個響應客戶端功能?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
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); }
觀察上面的代碼發現:
1 上面代碼的作用是將路由節點返回的數據封裝成Response對象等待發送
2 并且上面的代碼存在大量的instanceof判斷 (為什么要這樣呢 是因為一旦我們從控制器中返回一個實現了
laravel指定接口的實例,laravel就知道該如何渲染這些響應給客戶端 此時你可能還不清楚,請看下面的例子)
3 而且沒有else分支(這是因為laravel允許我們直接返回reponse對象,當我們直接返回Resposne實例的時候會直接走到方法的最后一句話)
4 并且最終都調用的都是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使用Reponse實現一個響應客戶端功能的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。