1. 程式人生 > >Swoole與跨域

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伺服器如nginxapache中進行修改解決,但是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;
    }

這裡我簡單的加入了幾個常用的MethodsHeaders,這樣實際上我們Swoole在接收到瀏覽器的跨域偵測時,就能返回正常的資訊並繼續跨域請求了。