为什么postman调接口不会跨域而浏览器会?
- 都在说跨域,为什么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
-
回调函数未定义,请求后端接口的script标签必须在声明的回调函数之后定义,否则会报错。
-
代码已push到侠梦的github,https://github.com/Hyq0823/Stardust/tree/master/src/main/java/com/cors