目录
●问题产生
最近笔者遇到一个项目,描述如下:对方给我们提供了一个Url,可以通过get请求返回一串字符串作为token,我们拿到这个token后,将作为其中一个参数,和其他参数一起组装成数据,在我们自己页面上点击按钮,通过ajax的post请求提交给我们自己的后台进行业务逻辑处理。
笔者首先通过浏览器直接访问这个Url,验证了确实可以拿到字符串token,因此,决定采用如下思路进行开发:第1步,页面点击按钮,触发点击事件,通过ajax的get请求拿到字符串token,其中返回的dataType设置为text类型;第2步,在其success对应的回调函数中,通过ajax的post请求将这个token作为参数一起组织成数据,提交给我们的后台。
$("#xxBtn").on("click",function(){
//第一步,Get请求
$.ajax({
type:"GET",
url : getRequestUrl,
dataType :'text',
success:function(response){
//第二步,Post请求
$.ajax({
type:"POST",
url : postRequestUrl,
dataType :'json',
data{
user:postuser,
name:postname,
token:response
},
success:function(response){
console.log(response.msg);
}
});
}
});
});
然而,诡异的事情出现了,通过浏览器F12查看确实发起了Get请求,并且状态码200,也有响应数据,但页面上死活不进入success的回调函数。后来经过查询知道,原来是跨域请求惹的祸。
解释一下什么是跨域请求。通常,我们开发的前端页面,发起的请求,无论是Get、Post、Put还是什么,其请求的Url大都是自己的系统,即同一个工程中。而上述例子中,请求的Url确是对方提供的系统,不属于我们自己的。基于安全因素的设计,前端Ajax做这种跨域请求时,网上提供两大类解决思路,分别对应于我们开发的前端以及对方的后端。其中最常见的是网上一搜“跨域请求”时几乎都有的方法(同质化太严重,都是互相copy的……),将返回类型dataType写为"jsonp",但是上述项目中却不适应,原因在于其返回的压根就不是Json对象,只是单纯的一个字符串,该方法淘汰。另一类是说让对方的系统设置运行跨域请求,添加我们请求源的Ip为信任地址,但该方法依然不适用,对方并不提供支持。这么看来似乎无解了,网上也查不到更多有效信息。
●问题解决
后来,和小伙伴讨论了一番,想到了另外一种解决思路,将第一步的Get请求,交给我们自己的后端去完成,拿到响应token后,直接和其他参数在后台一同处理。
首先验证了Java后端去做Get请求时是否会出现跨域问题,发现确实并不会出现前端那样的跨域请求问题,请求的代码如下:
public String sendGet(String url) {
String result = "";
BufferedReader in = null;
try {
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
相应地,前端的代码就修改成如下:
$("#xxBtn").on("click",function(){
//直接向后端发Post请求,数据不带token,token由后端去Get请求获取
$.ajax({
type:"POST",
url : postRequestUrl,
dataType :'json',
data{
user:postuser,
name:postname,
token:response
},
success:function(response){
console.log(response.msg);
}
});
});
最后,就是配置Struts或SpringMVC相关xml,接收前端请求,进行处理,返回前端的过程了,该部分涉及工作具体内容,就不举例了,大家可以结合自己的实际业务去编写代码。
●问题反思
确实,通过这种方式,能够解决跨域请求的问题。一方面提醒了我们遇到问题,可能会有不同的解决途径,需要勤加思索;另一方面也给我们敲了警钟,如果是前后端分离开发的项目,无法获得后端支持,前端真的就无解了吗?后来笔者继续学习,发现还是有不少解决思路的,例如通过代理等方式。今天,你学会了吗?