iT邦幫忙

2019 iT 邦幫忙鐵人賽

0
Software Development

Laravel 原始碼分析系列 第 34

如何正確地在 Response 加 Header(2)

續昨天,先來列一下 Http Kernel 預設有哪些 middleware:

protected $middleware = [
    \App\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,
];

// 為簡化問題,我們來看 web 的就好
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

但查過原始碼,雖然有的 middleware 有調整過 response 內容,但並沒有任何一個 middleware 去改過 header,理論上是不會受影響的。但我們應該用驗證來證實程式是如我們所預期的,因此筆者跟朋友確認程式內容,並實際做了一點實驗。

測試驗證

首先得知版本為 Laravel v5.7.0,我們先打開專案,來寫個 Feature 測試如下:

public function testBasicTest()
{
    $routeMiddleware = function ($request, $next) {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    };

    /** @var Router $router */
    $router = $this->app->make('router');
    $router->middleware(['web', $routeMiddleware])->get('/foo', function () {
        return '';
    });

    $this->get('/foo')
        ->assertStatus(200)
        ->assertHeader('Access-Control-Allow-Origin', '*')
        ->assertHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}

對於設定 Router 的方法有疑問的話,可以參考之前分析 Routing 文章。

這個測試是通過的,但畢竟這只是程式上運行,跟啟用 web 服務或許會有落差,所以實際照著朋友的做法做一次:

建立 Cors middleware:

<?php

namespace App\Http\Middleware;

class Cors
{
    public function handle($request, $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    }
}

Kernel 加上 alias:

protected $routeMiddleware = [
    // 略
    'cors' => \App\Http\Middleware\Cors::class,
];

routes/api.php 加上測試用 route:

Route::middleware(['api', 'cors'])->group(function() {
    Route::get('/foo', function () {
        return '';
    });
});

接著使用 Artisan 指令啟動服務:

php artisan serve

然後打開 http://localhost/api/foo 即可看到剛剛建立的測試 route。

驗證一切正常,該有的 header 是有的,所以可以推測是使用或測試的方法出錯導致錯誤的。


這篇記錄並不是責怪朋友,主要是想分享:這是從發現問題,到確認問題在或不在某個範圍內的過程。可以看到我們一開始從原始碼開始說明,但原始碼畢竟只是紙上談兵,所以也使用單元測試驗證,也做了實際整合程式的驗證。

開發程式的過程中,也會遇到許多奇怪的問題,我們可以像一開始一樣,直接翻原始碼確認,但時間容易拖很長,而且最後的結果依然不是可靠的;或許直接寫單元測試會是個可行的做法,寫測試即可馬上驗證想法是否正確,而且真的是一翻兩瞪眼啊!


上一篇
如何正確地在 Response 加 Header(1)
下一篇
自定義 bootstrapper
系列文
Laravel 原始碼分析46

尚未有邦友留言

立即登入留言