您的位置:首页 > 其它

让ajax跨域不再神秘(一)

2018-03-04 12:13 148 查看

前言

在实际工作中,项目经常会和其他项目进行接口对接,而有些接口调用需要我们使用ajax进行异步调用,这里就不可避免的涉及到ajax跨域访问问题,没有接触过ajax跨域请求的小伙伴可能对这块不太熟悉,这里来和大家一起学习一下ajax跨域问题,以及目前主流的解决措施。搞清楚到底什么是ajax跨域,不要总是处于一知半解的状态!

什么是ajax跨域

首先我们需要理解的是什么是ajax的跨域,产生跨域问题的三个要素如下

1. 是浏览器为了安全问题,而提出的同源策略,也就是浏览器会在前端对请求的进行安全校验,如果达不到安全级别就会报错,这里需要强调的一点是在前端进行校验,具体原因会在后面进行解释。

2. 是有ajax发出的XMLHttpRequest请求,这也就解释了为什么说是ajax跨域问题

3. 第三点就是指跨域请求,这里的跨域是指,如果请求的协议,ip地址,请求访问端口号这三者中有一个不一样就可称之为跨域

要想发生跨域请求问题,必须满足上面的三点,才会出现跨域请求报错。

下面有段简单的代码演示下,发生跨域请求的时候,浏览器会报什么错误,前端js脚本如下,这个js脚本和页面是部署在8081端口的tomcat上面

4000
function testAjax(){
$.ajax({
type : 'GET',
dataType : 'json',
url : 'http://localhost:8080/ajax1/ajaxTestController/testAjax', //服务路径
async : false,
success : function (data,status) {
if(status == 'success'){
alert('返回成功!');
}else{
alert('服务器异常!');
}
},
error : function (){
alert('服务器异常!');
}
});
}

<input type="button" onclick="testAjax();" value="跨域请求">


对应的后端服务如下,这个服务testAjax请求是部署在另外一个tomcat上面,端口是8080,这里利用端口号不同来模拟跨域

@ResponseBody
@RequestMapping("/testAjax")
public ResultBean testAjax(){
ResultBean result=new ResultBean();
result.setAge(18);
result.setName("爱琴孩");
result.setCode(200);
System.out.println("进入到这个跨域的controller中来");
return result;

}


下面是返回结果包装类

public class ResultBean  implements Serializable{
private static final long serialVersionUID = -8807978353119223457L;
private String name;
private Integer age;
private Integer code;
public ResultBean(){}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}
}


点击上面的跨域请求,具体异常如下



上面就是出现的ajax跨域访问问题,那么怎么解决呢,其实解决方式有多种,下面先介绍一个比较常见的jsonp解决方案

Jsonp解决ajax跨域请求

首先我们需要理解jsonp是怎么解决ajax的跨域请求,上面有说到产生跨域的三个原因,其中有一点是说请求是XMLHttpRequest,那么如果我们的请求不是XMLHttpRequest请求是不是就可以呢?其实jsonp就是和服务端进行约定,我传一个参数给你,你看这个参数就知道我请求不是XMLHttpRequest,jsonp动态的生成一段script标签,然后在script标签中再发送js请求,这样就避免上面的问题。

修改一下8081上面的ajax请求如下

function testJsonp(){
$.ajax({
type : 'GET',
dataType : 'jsonp',
jsonp : "callback",//这里和后台跨域服务约定callback作为方法名
url : 'http://localhost:8080/ajax1/ajaxTestController/testJsonp', //服务路径
async : false,
cache : true,
success : function (data,status) {
if(status == 'success'){
alert('返回成功!');
}else{
alert('服务器异常!');
}
},
error : function (){
alert('服务器异常!');
}
});
}


上面的jsonp的callback要和服务端的callback进行约定好,否则跨域请求还是会失败的

然后再修改一下8080上的服务端代码,如下

@ResponseBody
@RequestMapping("/testJsonp")
public String testJsonp(String callback){
ResultBean result=new ResultBean();
result.setAge(18);
result.setName("爱琴孩");
result.setCode(200);
System.out.println("用jsonp来进行跨域");
//注意这里将java对象转成json字符串,作为callback方法的参数
String resultStr=callback+"("+JSONObject.toJSONString(result)+")";
return resultStr;
}


细心的小伙伴可能看到上面的服务端代码,返回的是一个String类型的结果。请求结果如下



显然通过jsonp已经解决了跨域问题。

下面这张图可以对比一下,使用jsonp跨域和没有使用jsonp跨域的区别



没有使用jsonp的发送的请求是xhr,使用了jsonp的是script请求。同时我们还需要注意的一点即使是没有使用jsonp进行的跨域请求,该请求的响应码也是200,这也就解释了前面所说的ajax跨域是浏览器在前端进行验证的。

上面说到jsonp进行跨域是动态生成script标签,那么我们能不能验证一样这个script是不是生成了呢?我们可以在jquery的源码中打个断点,看看这个动态生成的script到底有没有生成。需要注意如果要自己测试,不要使用jquery的压缩版js,在1.11.3jquery的js中我们打断点如下



然后再次点击jsonp跨域请求按钮,我们可以看到如下动态生成script标签



Jsonp的优缺点

上面的例子可以看到jsonp解决跨域很简单,只需要修改部分代码就搞定,但是他也有一些问题,上面可以看到他需要修改跨域的服务端代码,我们在实际开发中可能不方便去修改别的项目的代码,那这种方式是不是就不行了呢,同时还有一种问题就是jsonp只是支持get请求,对于post请求却无能为力,这显然也是不行的。后续再和大家一起学习从隐身跨域和让浏览器支持跨域两个方面来解决ajax跨域问题。

好文推荐

个人能力有限,上面对jsonp的讲解可能不全面,这里推荐一篇文章,对jsonp讲解比较好,希望能有助于大家对于jsonp的理解

Java Ajax jsonp 跨域请求
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: