跨域问题
通过xhr进行ajax通信的一个主要限制是同源(同协议、同域名、同端口)策略。默认情况下,xhr只能访问与发起请求的页面在同一个域内的资源。这个安全策略可以防止某些恶意行为。
解决跨域的三种方法
我们在5000端口开启一个服务,需要做的是跨域请求服务端返回的数据。
sever1.js如下
const express=require('express');
const app=express();
app.use((request,response,next)=>{
console.log('有人请求server1啦!');
next();
});
app.get('/student',(request,response)=>{
const data={
'name':"amethyst",
"color":"blueviolet"
}
response.send(JSON.stringify(data));
});
app.listen(5000,()=>{
console.log('5000端口监听中...');
});
1、跨源资源共享(CORS)
CORS(Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信,其背后的思路就是使用自定义的HTTP头部允许浏览器和服务器相互了解,以确定请求或响应应该是成功还是失败。
这种方法我们只需要在服务端设置请求头即可
server1.js
app.get('/student',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*');
const data={
'name':"amethyst",
"color":"blueviolet"
}
response.send(JSON.stringify(data));
});
客户端可以直接请求5000端口的数据:
axios.get('http://localhost:5000/student').then((res)=>{
var pObj=document.createElement('p');
var con=res.data;
pObj.innerHTML=`
<h3>name: ${
con.name}</h3>
<h3>color: ${
con.color}</h3>
`;
document.body.appendChild(pObj);
}).catch(()=>alert('error'));
2、JSONP
jsonp利用了script标签不受同源策略限制的特点,通过动态创建script标签,并为src属性指定跨域url来实现的
jsonp看起来和json一样,只是会被包在一个函数回调里面,如:
callback({
"name":"lanlan"})
jsonp格式包含两个部分:回调和数据。回调是在页面接收到响应之后应该调用的函数,通常回调函数的名称是通过请求来动态指定的,而数据就是作为参数传给回调函数的json数据
jsonp服务通常支持以查询字符串形式指定回调函数的名称,比如以下整改例子的回调函数名称就被指定为handleResponse
客户端
var handleResponse=function(res){
console.log(res);
}
var script=document.createElement('script');
script.src='http://localhost:5000/student?callback=handleResponse';
document.body.insertBefore(script,document.body.firstChild);
服务端
app.get('/student',(request,response)=>{
const data={
'name':"amethyst",
"color":"blueviolet"
}
var str=JSON.stringify(data);
var cb=request.query.callback;
response.send(`${
cb}(${
str})`);
});
3、react脚手架配置代理(proxy)
我们的react项目在3000端口启动,现在仍然是要请求5000端口的数据。
产生跨域问题的本质是ajax引擎把从5000端口的响应体拦住了,而代理的作用就相当于一个中间人,转发ajax请求。由于代理服务器没有ajax引擎,并且是开在3000端口下的,因此可以接收从5000端口返回的数据并返回给3000端口,这样便解决了跨域问题。
第一种配置代理的方法
直接在package.json文件下配置proxy:
"proxy":"http://localhost:5000"
这样的话,客户端就直接向3000端口发送请求,如果请求的数据在3000端口,就直接返回响应体,否则代理服务器会再将请求转发给5000端口
handleClick(){
axios.get('http://localhost:3000/student').then((res)=>{
var pObj=document.createElement('p');
var con=res.data;
pObj.innerHTML=`
<h3>name: ${
con.name}</h3>
<h3>color: ${
con.color}</h3>
`;
document.body.appendChild(pObj);
}).catch(()=>alert('error'));
}
第二种配置代理的方式
新建一个setupProxy.js文件,在里面配置proxy,这种方法可以同时设置转发多个请求
const proxy=require('http-proxy-middleware');
module.exports=function(app){
app.use(
proxy('/api1',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{
'^/api1':''}
}),
proxy('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{
'^/api2':''}
})
);
}
遇见api1、api2就会触发该代理配置
target决定转发给谁
changeOrigin默认是false,控制服务器请求头中Host的值,当其值为true时,服务器收到的请求头中Host的值就是转发的目的端口,但是实际上来自于其他端口,这样就完美地欺骗了服务器,让其以为是自己端口下发出的请求
加入api2只是为了匹配端口发送代理,但是目的端口下是没有api2的,如第一个ajax请求,如果不通过pathRewrite将/api2替换成空字符串的话,代理将会转发请求到http://localhost:5000/api2/car,但是5001端口下并没有api2,这样就会报错。api1也是同理
axios.get('http://localhost:3000/api1/student').then((res)=>{
var pObj=document.createElement('p');
var con=res.data;
pObj.innerHTML=`
<h3>name: ${
con.name}</h3>
<h3>color: ${
con.color}</h3>
`;
document.body.appendChild(pObj);
}).catch(()=>alert('error'));
axios.get('http://localhost:3000/api2/car').then((res)=>{
var pObj=document.createElement('p');
var con=res.data;
pObj.innerHTML=`
<h3>name: ${
con.name}</h3>
<h3>color: ${
con.color}</h3>
`;
document.body.appendChild(pObj);
}).catch(()=>alert('error'));
请求地址
const {
response } = require('express');
const express=require('express');
const app=express();
app.use((request,response,next)=>{
console.log('有人请求服务器2啦!');
console.log('请求来自于',request.get('Host'));
console.log('请求的地址',request.url);
next();
})
app.get('/car',(request,response)=>{
const data={
"name":"lanlan",
"color":"pink"
}
response.send(JSON.stringify(data));
})
app.listen(5001,()=>{
console.log('5001端口监听中...');
})
这样便解决了跨域问题,同时也可以对多个端口发送请求~