ajax跨域完全讲解

1. 什么是跨越问题

1.1 什么是AJAX跨越问题

前台调用服务接口的时候,如果不属于同一个域的就会产生跨域问题。

 

2. 测试环境搭建

使用jquery和springboot搭建前后台测试环境,并引入前台jasmine自动测试框架,直观感受什么是跨域问题。

 

2.1 编写后台服务代码

基于springboot开发

2.2 编写前台页面代码

普通的前端页面

2.3 引入jasmine测试框架

 

3. AJAX跨域的原因分析

分析跨域问题产生的三个原因和解决思路,并结合系统的部署架构讲解,重点掌握问题分析的思路。

3.1 产生跨越问题的原因(3个条件同时满足才会产生跨域)

浏览器限制

跨域(协议、域名、端口 其中一个不一样都会造成跨域)

发送出去的请求是XHR(XMLHttpRequest)请求

 

4. 解决思路

4.1 解决思路

浏览器(浏览器禁止检查)

XHR(发送出去的请求不是XHR请求,可以使用jsonp)

跨域(被调用方(支持跨域)和调用方(隐藏跨域)来修改代码)

 

5. 全面解决跨域问题

重点讲解jsonp的工作机制,http协议如何支持跨域,以及http服务器nginx和apache的2种解决方案

5.1 浏览器禁止检查

进入chrome的启动文件路径下:

C:\Program Files (x86)\Google\Chrome\Application\chrome --disable-web-security --user-data-dir=g:\temp3

 

5.2 jsonp解决跨域(jsonp解决方式,需要修改前后端的代码)

前端:

$.ajax({

url: "http://localhost:8080/test/get1",

dataType: "jsonp",

  jsonp:"callback2",

  cache:true,//结果可以被缓存

success: function(json){

result = json;

}

});

 

后端(springboot):添加@ControllerAdvice注解的类

@ControllerAdvice

public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice{

public JsonpAdvice() {

super("callback2");

}

}

 

实现原理:(动态创建一个script,在script中把请求发送出去)

  • ajax请求类型是xhr请求,jsonp发送出去的ajax请求的type类型是script类型;
  • ajax请求返回类型是json,jsonp返回类型是js脚本;
  1. 的请求的url中带了一个callback参数。

 

JSONP有什么利弊

  •  
  • GET
  • XHR请求(XHR请求支持一些新的特性:异步,事件等)

 

5.3 最常见的javaee架构

 

5.4 跨域解决方向-被调用解决

解决方法:在响应头中增加指定的字段,告诉浏览器允许调用。

   可以在http服务器中添加响应头;

   也可以在应用服务器中添加响应头。

 

5.5 被调用解决 - filter解决方案

前端:正常的ajax请求;

后端:在filter中进行响应头的添加

 

@Override

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {

HttpServletResponse res =(HttpServletResponse) response;

res.addHeader("Access-Control-Allow-Origin", "*");//带cookie的跨域则使用*无法满足

res.addHeader("Access-Control-Allow-Methods", "*");

chain.doFilter(request, response);

}

 

5.6 简单请求和非简单请求(OPTIONS预检命令)

简单请求:就发送了一次请求,请求在浏览器上先执行后判断

方法为:GET,HEAD,POST

请求header里面:无自定义头;

Content-Type为以下几种:

text/plain

multipart/form-data

application/x-www-form-urlencoded

 

非简单请求:一共发送了两次请求,先发送一次OPTIONS预检命令,当通过后才发送真正的请求

方法为:put,delete方法的ajax请求

发送json格式的ajax请求

带自定义头的ajax请求

 

eg:发送json格式的ajax请求

后端:

controller:

@PostMapping("/postJson")

public ResultBean postJson(@RequestBody User user){

System.out.println("");

return new ResultBean("postJson " +user.getName());

}

 

过滤器:

@Override

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {

HttpServletResponse res =(HttpServletResponse) response;

res.addHeader("Access-Control-Allow-Origin", "*");

res.addHeader("Access-Control-Allow-Methods", "*");

res.addHeader("Access-Control-Allow-Headers", "*");

chain.doFilter(request, response);

}

 

前端:

$.ajax({

type : "post",

url: "http://localhost:8080/test/postJson",

contentType : "application/json;charset=utf-8",

data: JSON.stringify({name: "xiaofengqing"}),

success: function(json){

result = json;

}

});

 

5.7 简单请求和非简单请求(OPTIONS预检命令缓存)

 

设置预检命令:res.addHeader("Access-Control-Max-Age", "3600");

//在3600秒内不发送预检命令

 

5.8 带cookie的跨域

  1. 需要是被调用方的;
  • cookie的时候,Access-Control-Allow-Origin不能使用带*的;
  1.  

 

后端:

controller:

@GetMapping("/getCookie")

public ResultBean getCookie(@CookieValue(value="cookie1") String cookie1){

System.out.println("TestController.getCookie()");

return new ResultBean("getCookie " +cookie1);

}

 

过滤器:

@Override

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {

HttpServletResponse res =(HttpServletResponse) response;

HttpServletRequest req =(HttpServletRequest) request;

 

String origin = req.getHeader("Origin");

if (!StringUtils.isEmpty(origin)) {

//带cookie的时候,origin必须是全匹配,不能使用*

res.addHeader("Access-Control-Allow-Origin", origin);

}

res.addHeader("Access-Control-Allow-Methods", "*");

res.addHeader("Access-Control-Allow-Headers", "*");

res.addHeader("Access-Control-Max-Age", "3600");

 

//带cookie的跨域必须将这个设置为true

res.addHeader("Access-Control-Allow-Credentials", "true");

chain.doFilter(request, response);

}

 

前端:

$.ajax({

type : "get",

url: "http://localhost:8080/test/getCookie",

xhrFields:{

withCredentials:true

},

success: function(json){

result = json;

}

});

 

 

5.9 带自定义头的跨域

后端:

controller:

@GetMapping("/getHeader")

public ResultBean getHeader(@RequestHeader("x-header1") String header1,@RequestHeader("x-header2") String header2){

System.out.println("TestController.getHeader()");

return new ResultBean("getHeader " +header1+" "+header2);

}

 

过滤器:

@Override

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {

HttpServletResponse res =(HttpServletResponse) response;

HttpServletRequest req =(HttpServletRequest) request;

 

String origin = req.getHeader("Origin");

if (!StringUtils.isEmpty(origin)) {

//带cookie的时候,origin必须是全匹配,不能使用*

res.addHeader("Access-Control-Allow-Origin", origin);

}

res.addHeader("Access-Control-Allow-Methods", "*");

 

String headers = req.getHeader("Access-Control-Request-Headers");

if (!StringUtils.isEmpty(headers)) {

//支持所有自定义头

res.addHeader("Access-Control-Allow-Headers", headers);

}

 

res.addHeader("Access-Control-Max-Age", "3600");

 

//带cookie的跨域必须将这个设置为true

res.addHeader("Access-Control-Allow-Credentials", "true");

 

chain.doFilter(request, response);

}

 

前端:

$.ajax({

type : "get",

url: "http://localhost:8080/test/getHeader",

headers:{

"x-header1" : "AAA"

},

beforeSend: function(xhr){

xhr.setRequestHeader("x-header2","BBB")

},

success: function(json){

result = json;

}

});

 

 

 

5.10 被调用方解决跨域 - nginx解决方案

 

5.11 被调用方解决跨域 - apache解决方案_x264

 

 

5.12 被调用方解决跨域 - Spring框架解决方案

Spring框架中只需要添加@CrossOrigin注解即可

 

5.13 调用方解决跨域 - 隐藏跨域

实现原理:使用反向代理的方式,将被调用方的地址代理成一个相对地址,让浏览器认为是本域的地址。

在调用方的http服务器上进行代理配置

 

eg:

前端:$.getJSON("、ajaxserver/get1").then(function(jsonObj) {

result = jsonObj;

});

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值