Laravel中處理OPTIONS請求的原理是什么,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
成都創(chuàng)新互聯(lián)是專業(yè)的建華網(wǎng)站建設(shè)公司,建華接單;提供成都網(wǎng)站設(shè)計、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務;采用PHP框架,可快速的進行建華網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
Laravel處理OPTIONS
方式請求的機制是個謎。
假設(shè)我們請求的URL是http://localhost:8080/api/test
,請求方式是OPTIONS
。
如果請求的URL不存在相關(guān)的其它方式(如GET
或POST
)的請求,則會返回404 NOT FOUND
的錯誤。
如果存在相同URL的請求,會返回一個狀態(tài)碼為200
的成功響應,但沒有任何額外內(nèi)容。
舉例而言,在路由文件routes/api.php
中如果存在下面的定義,則以OPTIONS
方式調(diào)用/api/test
請求時,返回狀態(tài)碼為200
的成功響應。
Route::get('/test', 'TestController@test');
但同時通過分析可以發(fā)現(xiàn),這個OPTIONS
請求不會進到此api
路由文件的生命周期內(nèi),至少該GET
請求所在路由文件api
所綁定的中間件是沒有進入的。
此時如果手動添加一個OPTIONS
請求,比如:
Route::get('/test', 'TestController@test'); Route::options('/test', function(Request $request) { return response('abc'); });
則至少會進入該GET
請求所在路由文件api
綁定的中間件,可以在相關(guān)handle
函數(shù)中捕獲到這個請求。
通過仔細查看Laravel的源碼,發(fā)現(xiàn)了一些端倪。
在文件vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php
的第159
行左右,源碼內(nèi)容如下:
$routes = $this->get($request->getMethod()); // First, we will see if we can find a matching route for this current request // method. If we can, great, we can just return it so that it can be called // by the consumer. Otherwise we will check for routes with another verb. $route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) { return $route->bind($request); } // If no route was found we will now check if a matching route is specified by // another HTTP verb. If it is we will need to throw a MethodNotAllowed and // inform the user agent of which HTTP verb it should use for this route. $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) { return $this->getRouteForMethods($request, $others); } throw new NotFoundHttpException;
這里的邏輯是:
1. 首先根據(jù)當前HTTP方法(GET/POST/PUT/...)查找是否有匹配的路由,如果有(if(! is_null($route))
條件成立),非常好,綁定后直接返回,繼續(xù)此后的調(diào)用流程即可;
2. 否則,根據(jù)$request
的路由找到可能匹配的HTTP方法(即URL匹配,但是HTTP請求方式為其它品種的),如果count($others) > 0)
條件成立,則繼續(xù)進入$this->getRouteForMethods($request, $others);
方法;
3. 否則拋出NotFoundHttpException
,即上述說到的404 NOT FOUND
錯誤。
倘若走的是第2
步,則跳轉(zhuǎn)文件的234
行,可看到函數(shù)邏輯為:
protected function getRouteForMethods($request, array $methods) { if ($request->method() == 'OPTIONS') { return (new Route('OPTIONS', $request->path(), function () use ($methods) { return new Response('', 200, ['Allow' => implode(',', $methods)]); }))->bind($request); } $this->methodNotAllowed($methods); }
判斷如果請求方式是OPTIONS
,則返回狀態(tài)碼為200
的正確響應(但是沒有添加任何header
信息),否則返回一個methodNotAllowed
狀態(tài)碼為405
的錯誤(即請求方式不允許的情況)。
此處Laravel針對OPTIONS
方式的HTTP請求處理方式已經(jīng)固定了,這樣就有點頭疼,不知道在哪里添加代碼針對OPTIONS
請求的header
進行處理。最笨的方法是對跨域請求的每一個GET
或POST
請求都撰寫一個同名的OPTIONS
類型的路由。
解決方案有兩種,一種是添加中間件,一種是使用通配路由匹配方案。
總體思想都是在系統(tǒng)處理OPTIONS
請求的過程中添加相關(guān)header
信息。
3.1 中間件方案
在文件app/Http/Kernel.php
中,有兩處可以定義中間件。
第一處是總中間件$middleware
,任何請求都會通過這里;第二處是群組中間件middlewareGroups
,只有路由匹配上對應群組模式的才會通過這部分。
這是總中間件$middleware
的定義代碼:
protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\TrustProxies::class, ];
這是群組中間件$middlewareGroups
的定義代碼:
/** * The application's route middleware groups. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', \Illuminate\Session\Middleware\StartSession::class, ], ];
由于群組路由中間件是在路由匹配過程之后才進入,因此之前實驗中提及的OPTIONS
請求尚未通過此處中間件的handle
函數(shù),就已經(jīng)返回了。
因此我們添加的中間件,需要添加到$middleware
數(shù)組中,不能添加到api
群組路由中間件中。
在app/Http/Middleware
文件夾下新建PreflightResponse.php
文件:
getMethod() === 'OPTIONS'){ $origin = $request->header('ORIGIN', '*'); header("Access-Control-Allow-Origin: $origin"); header("Access-Control-Allow-Credentials: true"); header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE'); header('Access-Control-Allow-Headers: Origin, Access-Control-Request-Headers, SERVER_NAME, Access-Control-Allow-Headers, cache-control, token, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie, X-XSRF-TOKEN'); } return $next($request); } }
其中這里針對OPTIONS
請求的處理內(nèi)容是添加多個header
內(nèi)容,可根據(jù)實際需要修改相關(guān)處理邏輯:
$origin = $request->header('ORIGIN', '*'); header("Access-Control-Allow-Origin: $origin"); header("Access-Control-Allow-Credentials: true"); header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE'); header('Access-Control-Allow-Headers: Origin, Access-Control-Request-Headers, SERVER_NAME, Access-Control-Allow-Headers, cache-control, token, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie, X-XSRF-TOKEN');
至此,所有OPTIONS
方式的HTTP請求都得到了相關(guān)處理。
3.2 通配路由匹配方案
如果不使用中間件,查詢Laravel官方文檔Routing,可知如何在路由中使用正則表達式進行模式匹配。
Route::get('user/{id}/{name}', function ($id, $name) { // })->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
類似的,可以撰寫針對OPTIONS
類型請求的泛化處理路由條件:
Route::options('/{all}', function(Request $request) { return response('options here!'); })->where(['all' => '([a-zA-Z0-9-]|/)+']);
*注:這里正則表達式中不能使用符號*
因此,針對跨域問題,對于OPTIONS
方式的請求可以撰寫如下路由響應:
Route::options('/{all}', function(Request $request) { $origin = $request->header('ORIGIN', '*'); header("Access-Control-Allow-Origin: $origin"); header("Access-Control-Allow-Credentials: true"); header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE'); header('Access-Control-Allow-Headers: Origin, Access-Control-Request-Headers, SERVER_NAME, Access-Control-Allow-Headers, cache-control, token, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie'); })->where(['all' => '([a-zA-Z0-9-]|/)+']);
關(guān)于Laravel中處理OPTIONS請求的原理是什么問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。