跨域解决⽅案PHP,跨域解决⽅案
在进⾏前后端分离的开发中,跨域是⼀个不得不解决的问题。以下基于 Vue-Resource、PHP 及 Nginx 介绍跨域问题及其解决⽅案。跨域问题
配置
Nginx 中的配置只是简单的指向 PHP 代码的所在⽬录:
server{
listen 80;
server_name localhost;
root /mnt/apps;
index index.php index.html index.htm;
php中header是什么意思location / {
index index.php index.html;
}
location ~ \.php$ {
fastcgi_pass localhost:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
PHP 只是输出⼀个 JSON 数据,代码如下:
// api.php
header('Content-type : application/json');
$response = [
'key' => 'value'
];
echo json_encode($response);
Vue-Resource 为调⽤该接⼝,试图获取其中的数据:
this.$('localhost:8088/api.php').then(res => {
console.log(res);
})
测试
⾸先,我们在 Postman 中进⾏测试:
可以看到,这⼀接⼝是能够返回预期值的。
但当我们刷新 Vue 页⾯时,控制台中却没有输出想要的值,⽽是抛出了错误:
跨域问题
跨域问题基于浏览器的同源策略,简⽽⾔之,就是脚本不能调⽤来⾃不同域名、不同协议、不同端⼝的
资源。如上,来⾃ localhost:8080的 JavaScript 代码试图获取 localhost:8088 的 PHP 返回值(视为资源),便违背了同源策略,从⽽引发跨域问题。
有关同源策略的更多信息,可以参考知乎的这篇讨论:对于浏览器的同源策略你是怎样理解的呢?。
同源策略带来两个问题:
⽆法获取⾮本域的资源(⽆法调取 API 接⼝);
⽆法传递 Cookie;
以下提供三种⽅案以供参考。
PS:有同学可能会有疑问,为什么在 Postman 中不会有跨域问题呢?注意,跨域问题针对的是脚本对资源的访问限制,⽽ Postman 本⾝基于客户端代码,是 C/S 架构,⾃然不会有此问题。这就像是使⽤ curl 调⽤接⼝也不会受同源策略影响⼀样。
⽅案⼀:Jsonp
⽅案
当我们谈及获取⾮本域资源时,可以发现并不是所有类型的资源都受同源策略限制的,⽐如图⽚和 JavaScript、CSS 等。
这也使得我们可以转换思路,采⽤⼀种取巧的⽅式获得那些被同源策略拒绝的资源。⽐如,服务端动态地把数据放在 JavaScript 中,在前端请求时,将动态⽣成的 JavaScript ⽂件返回,⽂件中的内容包含相应的数据。
可是,单纯的在 JavaScript ⽂件中包含数据是不能被前端获取的。因为通过 JavaScript 代码是不能读取⽂件中的内容的。所以,我们的思路还需要转换⼀下。考虑以下场景:
function handle(data){
console.log(data);
}
引⽤的远程 JavaScript ⽂件为:
var json = {
key: 'value'
}
handle(json);
这样⼀来,通过这种⽅式,我们便在本地获取到了远程的数据。期间存在跨域,但没有违背同源策略。
也就是说,只要后台能够根据前台的请求,动态的⽣成⼀个调⽤特殊函数的 JavaScript ⽂件就可以了。具体流程如下:
在这其中,有⼏个问题需要解决:
JavaScript 代码如何才能向后台请求 JavaScript ⽂件?
前后端如何商议 handle 函数的名字?
先来看第⼀个问题。事实上,在 JavaScript 代码中是不能直接向后台索要 JavaScript ⽂件的。除⾮使⽤ DOM 操作创建⼀个 script 标签,再将请求地址通过 src 填充到该标签之中。可即使是这样,让后台动态⽣成 JavaScript ⽂件的⽅案还是不合适,这⽆疑增加了后台的负担。
不⽣成⽂件,如何实现函数调⽤和调⽤时传参呢?
在 JavaScript 中,我们可以使⽤ eval() 使得字符串具有特殊意义。如 eval("handle('data')") 可以使得中间的字符串变为 handle 函数的执⾏。这样⼀来,后台便不必再⽣成 JavaScript ⽂件,⽽只⽤发送字符串,再由前台通过 eval 处理即可。
这样⼀来,整个流程就变为了:
实现
采⽤原⽣⽅法实现时,我们需要准备⼀个接收函数(如 handle),以及在收到数据后使⽤ eval 将其包裹。这⼀过程实际上引⼊了很多与业务⽆关的代码。
借助 Vue-Resource 或 jQuery 等库,我们可以轻松地实现 jsonp:
将原先的 JavaScript 代码改变为:
this.$http.jsonp('localhost:8088/api.php').then(res => {
console.log(res);
})
并将 PHP 代码修改为:
header('Content-type : application/json');
$data = [
'key' => 'value'
];
$callback = $_GET['callback'];
$json = json_encode($data);
echo $callback.'('.$json.')';
测试
此时,我们可以在看到如下结果:
如此,我们便得到了想要的数据。
注意这⾥的请求 URL,Vue-Resource ⾃动帮我们加上了 callback 参数,即接收函数的名字。
如果我们想要⾃⾏指定接收参数的名字,或者在请求时添加额外的参数,可以使⽤如下⽅式:
this.$http.jsonp('localhost:8088/api.php', {
params: {
param1: 1
},
jsonp: 'callback'
}).then(res => {
console.log(res);
})
在浏览器的控制台中,我们可以到此次请求的⽹络传输过程:
可以看到,这⾥实际是发起了⼀次 GET 请求。
此外,由上图可知,使⽤ jsonp 的⽅式,Cookie 值也是可以成功传递的。
但是,这种做法其实是存在⼀些问题。因为需要适配 jsonp 的需求,返回值实际上变成了接收函数与实际数据的字符串拼接:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论