Swoole與跨域
來自我的部落格
Swoole是我經常在使用的PHP的協程高效能網路通訊引擎,非常好用,為PHP又提供了許多出乎意料的使用拓展,如攜程非同步等。並且是由C/C++
語言編寫,作為一個擴充套件安裝使用非常的簡單。
我在工作之餘嘗試將Swoole
和公司框架Gini
進行共同使用,效果不錯,可見兩者都符合低耦合的設計理念。簡單的增加了一個index.php
來作為程式的主入口,就非常簡單的使用起了大部分功能。
例如在接受http
請求方面:
public function request($req, $res) {
$header = $req->header ;
$_SERVER = array_merge($_SERVER, $req->server);
// TODO: 傳入res物件使後續可以對res物件進行操作
$uri = trim($_SERVER['request_uri'], '/');
$result = \Gini\CGI::request($uri, [
'header' => $header,
'get' => $req->get,
'post' => $req->post,
'files' => [], // 暫且先不考慮file
'route' => $uri,
'method' => $_SERVER['request_method'],
'swoole' => $this->server, // swoole_server 物件
'raw' => $req->rawContent(),
])->execute();
// 防止php://output過多擠壓supervisor.log
ob_start();
$result->output();
ob_end_clean ();
$res->status(http_response_code());
$res->end(J($result->content()));
}
就非常簡單的可以將Swoole
傳遞過來的請求,分發到Gini
框架的路由當中去,再做各自的邏輯處理。
但是當我部署到生產環境的時候,就出現了一件比較尷尬的事情,跨域警告了。
跨域
什麼是跨域呢,前端可能接觸的比較多,這並不是伺服器做出的限制,而是瀏覽器的安全策略。
瀏覽器為了防止不同域之間發生不安全的資料互動或者攻擊,會預設阻止這種行為。但是有些時候我們的應用的確是部署在不同的域下,這時候我們就要了解跨域的原理了。
當我們開啟除錯工具的時候,會發現在跨域請求之前,我們會看到一個類似這樣的請求
Request URL: http://path.to/api
Request Method: OPTIONS
但是沒有任何的訊息體進行返回,然後就報錯了,不能跨域。
實際上,瀏覽器在阻止跨域請求之前,會向目標伺服器傳送一個這樣的請求,來詢問伺服器:什麼樣子的請求可以跨域啊?
然後伺服器就會將自己的配置告訴瀏覽器,來方便做後續的判斷。這也就是網上大部分的解決方案有效的原因了,比如nginx
:
add_header Access-Control-Allow-Origin http://path.to;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
實際上就是告訴了瀏覽器,我允許跨域的Origin
, Headers
, Methods
等,如果請求符合,則就可以進行正常的跨域請求。
解決
這個在傳統的PHP
開發中很好解決,我們一般會在Web伺服器如nginx
或apache
中進行修改解決,但是Swoole
實際上自己充當了一部分Http
伺服器的角色,所以我們需要稍微做一下改動。
上面說到了,具體能否跨域,主要取決於OPTIONS
請求所返回的資訊,所以我們可以在這裡加以攔截:
public function request($req, $res) {
if ($req->server['request_method'] == 'OPTIONS') {
$res->status(http_response_code());
$res->end();
return;
}
...
}
這樣就可以終止後面的路由解析行為,防止後面的邏輯再做其他的處理,然後像nginx配置的那樣,我們需要加上我們希望返回的頭內容:
public function request($req, $res) {
// 跨域OPTIONS返回
$res->header('Access-Control-Allow-Origin', '*');
$res->header('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, PATCH, OPTIONS');
$res->header('Access-Control-Allow-Headers', 'Authorization, User-Agent, Keep-Alive, Content-Type, X-Requested-With');
if ($req->server['request_method'] == 'OPTIONS') {
$res->status(http_response_code());
$res->end();
return;
}
這裡我簡單的加入了幾個常用的Methods
和Headers
,這樣實際上我們Swoole
在接收到瀏覽器的跨域偵測時,就能返回正常的資訊並繼續跨域請求了。