AJAX跨域总结

三世轮回 提交于 2019-12-04 05:55:03

       蚂蚁金服的实习即将结束,将知识总结一下。

       我们这个项目前端使用antD,antD是采用React封装的一套组件库,目前开源http://ant.design/,所有组件都是拿来即用,大大缩短了开发周期,强烈推荐。React是单页面应用,通过ajax与后台通信,而antD调试部署在8000端口,后台又是运行在另一个端口,前后台通信跨域。AJAX跨域一般有两种解决方法:CORS(跨域资源共享)和JSONP。

       先来看看JSONP,本质原理利用script标签src属性可以跨域的特性,我们自己也可以去实现JSONP,动态添加删除script标签:

function loadJs() {
  var script = document.createElement("script");
  script.src = "http://xxxxxx/get/req";
  document.body.appendChild(script);
    
  script.onload = function() {
    callback(); 
    document.body.removeChild(script);
  }
} 

       在AJAX中使用JSOP:

$.ajax({
   url: 'http://xxxxxx/get/req', 
   cache: false,
   type: 'post',
   jsonp:'callback',
   dataType:'jsonp', 
   success: function (result) {
      //处理结果的过程
   },
   error: function (XMLHttpRequest) {
      //出错回调处理
   }
});

       相应的后台代码:

     /**
	 * 测试
	 * 
	 * @throws IOException
	 */
	@RequestMapping("/get/req")
	@ResponseBody
	public void getData(HttpServletRequest req, HttpServletResponse rep) throws IOException {
		Map<String, Object> result = null;
		result = manager.getDatas();
		String callback = req.getParameter("callback");
		String json = JSONObject.toJSONString(result);
		rep.setContentType("text/javascript");
		rep.setCharacterEncoding("utf-8");
		PrintWriter out = rep.getWriter();
		out.print(callback + "(" + json + ")");
	}

       CORS分为简单请求和非简单请求,非简单请求的请求方法不是POST、GET、HEAD,或者header中包含一些特殊请求头,以及Content-Type不是application/x-www-form-urlencoded、multipart/form-data、text/plain的请求。向后台发送请求时会多发送一次option请求,并且不能携带cookie,这个后面再说。CORS只需服务器端在每次响应中设置一些响应头即可,将这些操作放在Filter中实现低耦合:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse rep = (HttpServletResponse) response;
        HttpServletRequest req = (HttpServletRequest) request;
        rep.addHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
        rep.setHeader("Access-Control-Allow-Credentials", "true");//跨域携带cookie
        rep.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");//非简单请求
        rep.setContentType("application/json; charset=utf-8");
        rep.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE");//非简单请求
        chain.doFilter(request, rep);
    }

       我们的项目需要接入统一登录中心,SSO大家也知道,每次请求来了之后SSOClient判断是否携带token,过期或者没有token会重定向到登录页面让用户进行登录。这里出现了一个问题,前端向后台发送AJAX请求,在用户没有登录时并不能正常重定向到登录中心,而是出现前台到登录中心页面的XMLHttpRequest跨域错误提示。但是通过浏览器直接输入后台请求URL地址,在用户没有登录时,会重定向到登录中心页面。对于这个问题的产生一开始没有头绪,进行了多种尝试之后定位到了原因。

       这里涉及到浏览器是如何处理AJAX请求重定向,当服务器将302响应发给浏览器,浏览器并不是直接进行AJAX回调处理,而是先执行302重定向,从Response Headers中读取Location信息,然后向Location中的Url发出请求,在收到这个请求的响应后才会进行AJAX回调处理。antD是个单页面应用,前后台交互通过AJAX的方式,所以当SSOFilter拦截ajax请求之后,浏览器会首先重定向到登陆中心,产生了前台到登录中心的跨域AJAX请求,当浏览器发现响应头信息没有包含Access-Control-Allow-Origin字段,从而抛出XMLHttpRequest cannot load的错误。

       CORS请求默认不发送Cookie和http认证信息,如果要把Cookie发送到服务器,一方面需要服务器同意,指定Access-Control-Allow-Credentials响应头,另一方面必须在AJAX中打开withCredentials属性,$.ajaxSetup方法设置AJAX请求的默认参数选项,多个AJAX请求时,不用为每一个请求配置请求的参数:

 $.ajaxSetup({
   xhrFields: {
     withCredentials: true
   },
 })

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!