关于Ajax跨域的解决方案和一些个人理解
Ajax跨域的原因
- 浏览器限制
- 跨域(协议、主机名、端口有一个不同就会产生跨域)
- xhr请求(XMLHttpRequest)
当以上三个条件同时满足时才会产生ajax跨域
Ajax跨域的解决方案
只要破坏上述三个条件之一就可以解决跨域问题
-
浏览器的启动参数上来设置,使浏览器不做跨域校验(不常用)
以谷歌浏览器为例,在chrome.exe所在的路径按下shift键,点击右键,点击“在此处打开命令行窗口”,然后输入chrome --disable-web-security就可以了,此方法不常用,因为需要在客户端操作,而大多数用户是不会操作的 -
xhr解决==》jsonp(不常用)
以下是谷歌浏览器中如何查看是否xhr请求(按F12,然后在弹出框中点击NetWork的All)
使用jsonp来传输,jsonp会把动态创建script标签来进行请求,而对于服务器后台返回的数据用scprit标签形式进行解析(此时后台必须进行改动返回jsonp数据,否则前台会解析异常)js代码:
$.ajax({ url: "http://localhost:8080/ajaxTest", dataType: "jsonp", jsonp: "callback", // 此处的字段应和后台统一,否则不会返回jsonp格式 success: function (json) { alert(json) } })
后台接口:
@RequestMapping(value = "/ajaxTest") public Result ajaxTest(){ System.out.println("ajaxtest方法被调用了"); return ResultUtil.success(); }
浏览器控制台:
Uncaught SyntaxError: Unexpected token(这里是解析异常)
服务器给出的json数据依旧能拿到,但控制台会报出解析异常,因为Jsonp解析的是script字段,此时应对后台接口进行改动(此处改动是基于springboot的):
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; @ControllerAdvice // AbstractJsonpResponseBodyAdvice在Springboot 2.x已废弃 public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice{ public JsonpAdvice() { super("callback"); // 此处字段和上边Ajax的jsonp应统一 } }
jsonp的弊端:
- 服务器代码需要改动(自己的项目可以改,如果是别人的项目那就。。。)
- 只能支持get请求(请求和返回的都是script代码)
- 不是xhr请求
-
跨域方面解决(常用)
对于跨域的请求,浏览器会在请求头上加上Origin,而在服务器返回数据时判断响应头有无跨域信息,若没有则会报错
被调用方解决跨域:- 服务器端实现
使用Filter来进行处理
- 服务器端实现
// 注册filter
@Bean
public FilterRegistrationBean registerFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
// 设置所有请求都经过过滤器
bean.addUrlPatterns("/*");
// 设置添加的过滤器
bean.setFilter(new CrosFilter());
return bean;
}
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 设定允许请求的域
response.addHeader("Access-Control-Allow-Origin","*");
// 设定允许请求的头(简单请求不需要加,非简单请求需要带上)
response.addHeader("Access-Control-Allow-Headers","*");
// 设定允许请求的方法
response.addHeader("Access-Control-Allow-Methods","*");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
带cookie的跨域filter设置(对于带自定义请求头的跨域,只许要在过滤器返回中添加对应请求头)
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse)servletResponse;
HttpServletRequest request = (HttpServletRequest)servletRequest;
String origin = request.getHeader("Origin");
if(!StringUtils.isEmpty(origin)){
// 设定允许请求的域
response.addHeader("Access-Control-Allow-Origin",origin);
}
// 设定允许请求的头
response.addHeader("Access-Control-Allow-Headers","Content-Type");
// 设定允许请求的方法
response.addHeader("Access-Control-Allow-Methods","*");
response.addHeader("Access-Control-Max-Age","3600");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
- Nginx配置
待添加— - Apache配置
待添加---- - spring框架的跨域注解
@CrossOrgin
可以加在类或方法上来解决跨域
调用方解决
Nginx反向代理
Apache方向代理