由于安全上的限制,在浏览器中使用 JavaScript 不能直接访问跨域的资源。我们可以通过服务器来中转:用 PHP 编写一个简单地跨域访问代理。
一般需要支持 GET 和 POST 这两种 HTTP 请求。对于 GET 方式,我们可以用 fopen,file_get_contents 和 curl 来抓取资源。在网上查询得知 curl 速度会快一些,而且会缓存 DNS 信息,所以就使用 curl 来处理。例子如下:
function curlget($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $data = curl_exec($ch); if ($data === false) { header('HTTP/1.1 400 Bad Request'); exit; } else { echo $data; }
我们也可以用 curl 来处理 POST 请求。我们至少需要区分两种 POST 请求:第一种的 Content-Type 为 application/x-www-form-urlencoded,仅仅用于提交参数;而第二种的 Content-Type 为 multipart/form-data,用于上传文件。POST 请求的例子如下:
function curlpost($url, $data) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $data = curl_exec($ch); if ($data === false) { header('HTTP/1.1 400 Bad Request'); exit; } else { echo $data; }
其中 curl_setopt 的 CURLOPT_POSTFIELDS 选项中,如果 $data 是字符串,curl 提交时将设置 Content-Type 为 application/x-www-form-urlencoded;如果 $data 是包含 'file' 项的数组,curl 提交时将设置 Content-Type 为 multipart/form-data。
现在我们假定:客服端将要访问的地址作为 URL 的 path 参数发送过来,即 JavaScript 代码类似于下面:
var path = 'http://some.domain.com/some/page/'; var url = 'http://example.com/?path=' + encodeURIComponent(url);
在 PHP 中,不管是 GET 方式还是 POST 方式的请求,URL 中的参数(即上面的 path 值)都放在 $_GET 数组中。而 POST 方式提交的数据,如果是 application/x-www-form-urlencoded 的请求,放在 $_POST 数组中;如果是 multipart/form-data 的请求,所上传的文件存放在临时目录中,文件的基本信息放在 $_FILES 数组中。理解了这些知识后,我们可以用下面的代码来处理请求:
var $url = $_GET['path']; switch ($_SERVER['REQUEST_METHOD']) { case "GET": curlget($url); break; case "POST": switch($_SERVER['CONTENT_TYPE']) { case 'application/x-www-form-urlencoded': $data = http_build_request($_POST); break; case 'multipart/form-data': $tempname = $_FILES['file']['tmp_name']; $basename = basename($_FILES['file']['name']); $origname = sys_get_temp_dir() . '/' . $basename; move_uploaded_file($tempname, $origname); chdir(sys_get_temp_dir()); $data = array('name' => 'file', 'filename' => '@' . $basename); break; default: exit; } curlpost($url, $data); break; default: exit; }
其中使用 chdir 函数是为了保证 curl 提交文件时不包含路径。
参考资料:
[1] JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls
[2] PHP 手册:file_get_contents
[3] PHP 手册:cURL 函数
[4] file_get_contents VS CURL, what has better performance?
[5] 简评file_get_contents与curl 效率及稳定性
[6] PHP 手册:$_GET
[7] PHP 手册:$_POST
[8] PHP 手册:$_FILES
[9] PHP 手册:$_SERVER
[A] PHP 手册:http_build_query
[B] PHP 手册:move_uploaded_file
[C] HTML 4.01 Specification - Forms
[D] Get “Content-Type” header of request in PHP
[E] Form Data Processing
[F] PHP 文件上传
[G] arkadi / phpproxy / source - Bitbucket
[H] CORS Proxy
[I] node.js based CORS proxy