都在说跨域,为什么postman能访问接口,而浏览器就不行呢?这里需要理解什么是跨域,跨域是指的当前资源访问其他资源时发起的http请求由于安全原因(由于同源策略,域名、协议。端口中只要有一个不同就不同源),浏览器限制了这些请求的正常访问,特别需要注意的是这些发生在浏览器中。而通过postman等工具调用接口时,只是简单的访问一个资源,并不存在资源的相互访问。
知识预热
·回调函数
这里就不细讲回调函数啦,相信各位都能理解,用一个案例来说一下。
“服务器”和“客户端”初次相识,相谈甚欢,客户端希望服务器把一个叫json的东西给它,”服务器“:你先忙着,我弄好了发给你,把你电话号码(回调函数)给我。
跨域中的预检请求即是指浏览器在真正发送请求前,会先发送一个Options请求嗅探,请求成功后才会发送真实的请求。
问答三连:
为什么需要发送预检请求?是因为触发了浏览器的安全校验。
为什么请求会触发安全校验?因为当前请求是一个"复杂请求"。
为什么我的请求是“复杂的”?见下面解释——
·复杂和简单请求
简单请求:请求方法是GET/HEAD/POST,并且contentType为text/plain、application/x-www-form-urlencoded、multipart/form-data。
不满足上述条件的视为复杂请求,开发中我们常触发这个条件大多因为我们的请求的contentType设置的是application/json导致的。
简单请求如果设置了Authentication认证header也会让请求“升级”为复杂请求。
理解spring提供的@CrossOrigin注解
@CrossOrigin(maxAge = 3600,origins = "*") |
@CrossOrigin可以放在某个方法上,或者放在类上,这样对类中所有请求方法有效,指定能访问的域集合,即是设置Access-Control-Allow-Origin。
maxAge 属性指定了预检请求的缓存时间,单位是秒。对应http的Access-Control-Max-Age属性。
缓存的内容为:Access-Control-Allow-Methods和Access-Control-Allow-Headers 提供的信息。
理解使用jsonp解决跨域问题
上面简单介绍了回调函数,jsonp是如何解决跨域的呢?其实就是利用了一个特性,html页面中引入不同域的js是不会引发跨域的,下面的案例中,我引入了一个在线的query地址,并且在script标签的src写上了后端的接口地址,从前端的角度我们可以想象成只是简单请求一个js文件,js文件的内容是一个函数调用,刚好浏览器本地定义了这个函数,所以请求完毕,就会对这个函数发起调用,当然前后端的函数名需要一样,这就是我们说的回调函数的机制。
前端 <html> <body> <head> <script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script> </head> <h3 id="test">init...</h3> </body> <script type="text/javascript"> //定义回调函数,相当于上述说的“电话号码” function myCallback(res){ alert(JSON.stringify(res)); } </script> <script src="http://localhost:9992/cors/infoWithCallBack"></script> -------------------------------------------------------------------------------- 后端: /** * 返回 使用回调函数包裹的json * @return */ @RequestMapping("/infoWithCallBack") public String info(){ JSONObject map = new JSONObject(); map.put("msg","请求成功!"); map.put("code","0000"); map.put("data","获取到后端数据!"); String str = map.toString(); return "myCallback(" + str + ")"; } |
上面的例子在正常返回的json前面包裹了一个函数名,为的是能让浏览器执行,如果不采用jsonp的方式,而是后端设置Access-Control-Allow-Origin,则请求到的不是一个标准json,所以我们需要改造一下,兼容两种方式。
<script type="text/javascript"> $(function(){ $.ajax({ type:'POST' ,dataType:"jsonp" ,url:'http://localhost:9992/cors/info3' ,data:{} ,contentType:'application/json;charset=utf-8' //,jsonp:"callback" //指定回调函数的名字。一般不改这个,默认callback //,jsonpCallback:"myCallback" //手动执行发送给服务器的callback参数名,否则由jquery自动帮我们生成,类似jQuery17207352806672191685_1544278219377 ,success:function(data){ debugger; alert("ajax: "+JSON.stringify(data)); } }); } </script> -------------------------------------------------------------------------------- /** * 3.兼容jsonp和普通json请求 * @return */ @RequestMapping("/info3") public String info3(@RequestParam(name="callback",required = false) String callBack){ JSONObject map = new JSONObject(); map.put("msg","请求成功!"); map.put("code","0000"); map.put("data","获取到后端数据!"); String result = map.toString(); if(!StringUtils.isEmpty(callBack)){ //如果是json请求,则包裹上回调函数 return callBack + "(" + result + ")"; } return result; } |
其他遇到的问题
Uncaught SyntaxError: Unexpected token : |
在解决跨域时,遇到这种错误,一般是前端没有设置回调函数,而后端返回了被回调函数包裹的json。
info?callback=callback:1 Uncaught ReferenceError: callback is not defined at info?callback=callback:1 |
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理