最近测试一些东西,需要跨域访问Restful接口,因为是测试,不可能改变接口形式,所以jsonp方案直接Pass。

本地测试时简单写了几行NodeJS代码,对特定路径下的请求进行转发后回传,因为使用WebStorm开发,NodeJS不可以和WebStorm内置服务器使用相同端口,虽然可以通过配置本地服务器,完全将NodeJS作为服务器,但那样需要解决静态资源的输出,写东西过多,偏离初衷,这里不再赘述。

不过如果你是做前后端完全分离的项目,大可以找个开源的Web框架来用,比如express等,这里不多介绍。

总之但思路就这样了,可能不太完善,代码如下:

/**
 * Created by William.Wei on 2016/4/23.
 */
var http = require('http');

var server = http.createServer();

server.on('request', function (request, response) {
    console.log(request.url);
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Content-type', 'text/html;charset=UTF-8');
    http.request({
        hostname: 'www.poorren.com',
        port: 80,
        path: request.url,
        method: request.method
    }, function (res) {
        res.on('data', function (data) {
            response.write(data);
        });
        res.on('end', function () {
            response.end();
        });
    }).end();
});

server.listen(88);

如上,因为是不同端口,所以本质上采用的CORS,所以仅仅是中转思路而已,通过Web端ajax入口url统一处理后访问端口为88的地址即可。

本地没问题了,想放到外网玩呢?如果有托管主机或者VPS啥的还好,个人是不喜欢折腾,平时测些简单的页面啥的,直接丢虚拟主机,因为目前可以跑NodeJS的虚拟主机寥寥无几,就想到了PHP,刚好公司有人封装有Restful请求工具,参考源代码简化处理了一下,最终代码如下:

<?php
class Rest
{

    private $baseURL;
    private $http_headers = [];
    private $curl_opts;
    private $method;
    private $params = [];
    private $urlParam = "";
    private $httpHeader = [];
    private $acceptXmlType = "Accept:application/xml";
    private $acceptJsonType = "Accept:application/json";

    public function __construct($method, $baseURL = '')
    {
        $this->baseURL = $baseURL;
        $this->method = $method;
        $this->curl_opts = array(
            CURLOPT_CONNECTTIMEOUT => 60,   /* 在发起连接前等待的时间,如果设置0,则无限等待 */
            CURLOPT_TIMEOUT => 300,  /* 允许执行的最长秒 */
            CURLOPT_USERAGENT => 'poorren.com',
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        );
    }


    private function getUrlParams()
    {
        return $this->urlParam;
    }

    private function getSetting()
    {
        if ($this->method == "POST") {
            // post数据
            $this->curl_opts[CURLOPT_POST] = true;
            if (is_string($this->params)) {
                $this->curl_opts[CURLOPT_POSTFIELDS] = $this->params;
            } else {
                $this->curl_opts[CURLOPT_POSTFIELDS] = json_encode($this->params, JSON_UNESCAPED_UNICODE);
            }
        } else if ($this->method == "PUT") {
            // put数据
            $fields = is_array($this->params) ? http_build_query($this->params) : $this->params;
            $this->curl_opts[CURLOPT_CUSTOMREQUEST] = 'PUT';
            $this->curl_opts[CURLOPT_RETURNTRANSFER] = true;
            $this->curl_opts[CURLOPT_POSTFIELDS] = $fields;
        } else {
            $urlParam = "";
            if ($this->params != null) {
                foreach ($this->params as $key => $value) {
                    $urlParam = $urlParam . "$key=" . urlencode($value) . "&";
                }
            }
            $this->urlParam = $urlParam;
        }

        $this->curl_opts[CURLOPT_HTTPHEADER] = $this->httpHeader;

        return $this->curl_opts;
    }

    private function execute($resource)
    {
        $ch = curl_init();
        curl_setopt_array($ch, $this->getSetting());
        if (!empty($this->http_headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $this->http_headers);
        }
        $urlParams = $this->getUrlParams();
        if (strpos($this->baseURL . $resource, '?') > 0) {
            $requestUrl = $this->baseURL . $resource . "&" . $urlParams;
        } else {
            $requestUrl = $this->baseURL . $resource . "?" . $urlParams;
        }

        curl_setopt($ch, CURLOPT_URL, $requestUrl);

        $result = curl_exec($ch);
        $code = curl_errno($ch);
        if ($code != 0) {
            $msg = curl_error($ch) . " request url is ({$code}):" . $requestUrl;
            throw new Exception($msg, $code);
        }

        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($http_code != 200) {
            throw new Exception($requestUrl);
        }

        return $result;
    }

    public function invoke($path, $params, $jsonParse = true, $acceptType = 'json')
    {
        $this->params = $params;
        if ($acceptType == 'xml') {
            $this->httpHeader[0] = $this->acceptXmlType;
        } else {
            $this->httpHeader[0] = $this->acceptJsonType;
        }
        $content = $this->execute($path);
        return $jsonParse ? json_decode($content) : $content;
    }

    public function setHeaders($headers)
    {
        if (empty($headers)) {
            return;
        }

        foreach ($headers as $k => $v) {
            $this->http_headers[$k] = $v;
        }
    }
}

仔细看的同学可能发现了,这个貌似和上次发的图片代理《两行代码绕过基于referrer的防盗链》有类似作用,感兴趣的同学可以以此为基础来改造一下,就是个全功能图片代理。

以上为开发、测试用代码,仅分享,不建议使用到生产环境。