
# Laravel 使用 Middleware 解決 ‘Access-Control-Allow-Origin’ 的問題
# 出國有很多方法,偷渡是一種
如果有在寫網頁程式相關的,多少都會碰過 No ‘Access-Control-Allow-Origin’。這簡單來說是瀏覽器同源策略 (same-origin policy) 下的產物。
所謂的跨域 (cross domain),就是跨到其他了的網域。這不僅僅是不同的網域名稱,也可能是  同個域名,不同端口(port)  。瀏覽器存取時是有規則的,只要  協議  (通常指的是 http、https, …)、  域名  、  端口  有任何一個不同,就會被當作是不同的域。
以下以表格條列展示(假設原始網域為 host1.com )
| URL | 是否成功 | 註解 | 
|---|---|---|
| http://host1.com/arg1 | 成功 | |
| http://host1.com:8000/arg1 | 失敗 | 不同端口 (port) | 
| http://host2.com/arg1 | 失敗 | 不同網域 | 
| https://host1.com/arg1 | 失敗 | 不同協議 | 
瀏覽器阻擋跨網域的目的主要是為了安全性,不讓你任意存取非本網域 (domain) 的資料。
試想,假如你在 A 網站,用一段 js 腳本就能訪問 B 網站,B 網站憑什麽讓你訪問,為什麽瀏覽器能讓你隨便訪問別的網站呢,說不定 B 網站存在著各種危險,比如盜取你的密碼,跨域攻擊 (CSRF) 等,這並不太合理。
但也是有例外的情況,有些情況是要訪問到別的網站的,比如加載一張圖片,這張圖片可能在別的網站,還有加載一個 js 文件,也是有可能在別的網站的,還有嵌入一個 frame 元素,也需要訪問到別的網站的內容。
因此,瀏覽器協議中也為此制訂了對策 — CORS
CORS 全名是 Cross-Origin Resource Sharing,跨域資源共享,這是瀏覽器的標準,也算是協議,基本上現代瀏覽器都支持,除了奇葩瀏覽器,例如 IE8、IE9,只支持部分特性。
以下將講解如何設定 Laravel,使得不同網域的前端可以跨網域 request 所需的資料。
# Laravel
首先我們先新創建一個 Middleware 叫做 Cors
php artisan make:middleware Cors
創建好之後,應該會看到 Laravel 已經建立好基本的板模了

| <?phpnamespace App\Http\Middleware;use Closure;class Cors | |
| {/** | |
| * Handle an incoming request. | |
| * | |
| * @param \Illuminate\Http\Request $request | |
| * @param \Closure $next | |
| * @return mixed | |
| */ | |
| public function handle($request, Closure $next) | |
|     { | |
|         // | |
| return $next($request); | |
|     } | |
| } | 
由於我們只希望同樣 domain name 的前端可以存取我們的 api ,因此我們在 Cors 只對相同 domain name 的前端允許  'Access-Control-Allow-Origin'  ,我們在 handle 這個 function 中加入規則後,程式碼應該會長成這樣。
| public function handle($request, Closure $next) | |
| { | |
|     // 設定允許訪問的 domain address | |
| $host = $request->getHost(); // returns [dev.site.com](http://dev.site.com/) | |
| $domains = [$host.':8080', $host.':80'];// 判斷 request 的 header 中是否包含 'ORIGIN' | |
| if ( isset( $request->server()['HTTP_ORIGIN'] ) ) {$origin = $request->server()['HTTP_ORIGIN'];// 如果 origin 帶有 http, https 則把它濾掉 | |
| $pattern = ""; | |
| if (preg_match('#^https?://#', $origin)) { | |
| $pattern = preg_replace('#^https?://#', '', $origin); | |
| }if ( in_array( $pattern, $domains ) ) { | |
|             // 設定 response header 的信息 | |
| return $next($request) | |
| ->header('Access-Control-Allow-Origin', $origin) | |
| ->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Authorization') | |
| ->header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); | |
|         } | |
| }return $next($request); | |
| } | 
寫好了 Cors 的 Middleware 後,我們將其加入我們的 Middleware 列表。修改  Kernel.php ,找到
| protected $routeMiddleware = [ | |
| 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, | |
| 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, | |
| 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, | |
| 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, | |
| 'can' => \Illuminate\Auth\Middleware\Authorize::class, | |
| 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, | |
| 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, | |
| ]; | 
並加入下面這行
| 'cors' => \App\Http\Middleware\Cors::class | 
最後,我們要讓我們 Route 使用我們寫的 Middleware,因此在  web.php  中,我們所使用的 route 加入我們寫的 middleware,以下以 route::group  為範例:
| Route::group([ | |
| 'middleware' => ['api', 'cors'], | |
| 'namespace' => $this->namespace, | |
| 'prefix' => 'api', | |
| ], function ($router) { | |
|          // some code here | |
| }); | 
如此,我們便設定好了讓相同  domain name  的前端可以存取我們 Laravel server 的資源的規則了。
# 參考目錄
- Adding Access-Control-Allow-Origin header response in Laravel 5.3 Passport
 
        