什么是跨域
简单来说就是请求的地址与页面地址的协议,域名,端口号只要其中之一不一样就属于跨域,跨域情况下如果不进行处理就不能获得请求后的数据,为什么说是请求后的呢而不是不能请求,因为跨域情况下请求也是到了后台的进入了对应action,在后台返回http头中如果没有相对应的字段就表示后台不允许跨域,浏览器才会出于安全原因拒绝跨域。
跨域问题产生的原因
浏览器限制 –>并不是后台程序不允许访问
跨域 –>域名,端口号,协议之一不一样就可满足
XHR(XMLHttpRequest)请求 –>可扩展超文本传输请求(页面局部刷新的请求)
三个问题同时满足才能产生跨域 –>所以跨域不一定产生跨域问题
浏览器限制问题
解决浏览器限制并不是好的方法–不可能说叫所有使用的用户都去进行浏览器设置 详细操作可看https://www.baidu.com/s?wd=允许谷歌跨域访问
springmvc框架的语法糖
若你使用的是springmvc框架 那真是太幸运了 为什么了因为该框架为我们提供了一个注解我们只需要在controller中添加一个注解@CrossOrigin(作用于类与方法)就可以解决跨域的一系列问题啦
如果你是初次使用,建议还是详细看看下面的讲解,我们不能依赖于框架,如果哪天换了一个框架,岂不是两眼一抹黑,不知道该如何是好了呀
使用XHR请求并跨域
在跨域情况下的请求是到达了后台了的,所以我们要解决跨域根源上需要后台通知浏览器我允许他的跨域请求,这时跨域问题就得以解决。所以我们需要在返回浏览器数据中添加对应的http头就可以了。我们使用filter过滤器进行对所有请求或者部分请求添加http请求头
过滤器中添加具体代码如下
1 | res.setHeader("Access-Control-Allow-Origin","*"); //指定所有请求源都运行跨域请求 也可以指定自定义的url |
这样我们便实现了跨域问题但是这样我们有一个严重的问题就是 不能获取cookie也就是同一次的会话每次session都是不一样的。
获得cookie
获得cookie我们一样的需要在过滤器中的HttpServletResponse中添加
1 | res.setHeader("Access-Control-Allow-Credentials","true"); |
在前台请求中需要如下格式
1 | $.ajax({ |
注意如果添加以上http头,我们的请求源则不能设置成通配符了
如上所示那么难道说我们只能设置指定的请求源吗答案当然是否定的,我们在观察请求头时发现每次请求在请求头中都有Origin字段它的值便是请求源,所以我们只需要在HttpServletRequest里面取出请求的origin值在设置进返回的http头中这样就实现了相当于通配符的功能了
具体操作如下
1 | HttpServletRequest req= (HttpServletRequest) servletRequest; |
这样设置后我们所有的跨域请求都能执行cookie访问了
另外如果我们每次的ajax请求都去添加 xhrFields:{withCredentials:true}很麻烦的,我们可以将它同意配置即可,在配置文件中添加如下代码 这样我们就可以不用每次都配置啦
1 | $.ajaxSetup({ |
不用XHR请求–jsonp
jsonp 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。也就是说我们请求的数据实际上为javascript数据,所以浏览器不会阻止我们的跨域请求
前台请求格式如下 –使用jquery
1 | $.ajax({ |
后台设置为 –这里使用的是spring框架
1 | @ControllerAdvice |
我们先看一下实际执行结果
可见能够请求并且获得返回数据
在这里我们看出我们的请求不是XHR请求而是js请求 并且我们发生的参数多了两个callback与_ 这里的简单的说下这两个参数的作用,callback表示的是返回数据的函数名,我们说了jsonp是以js格式访问的,所以返回的数据实际上是一段js函数,函数名就是callback指定参数后面的值,该参数是jsonp的默认参数名,我们可以通过ajax参数修改在ajax函数里面添加上jsonp:”methodName”即可 同时要记得将后台中的callback与前台相对应即可
1 | @ControllerAdvice |
另外虽然说后台返回的数据为js函数但是jquery已经将js函数处理了依旧转换为json格式数据,后台原本返回数据为
1 | jQuery19107031051372733508_1524928757925({"name":"456","nm":0,"no":null}); |
_ 参数则为时间戳用来防止浏览器缓存–>短时间类相同请求浏览器不会重复请求。
简单请求与非简单请求
简单请求先执行后判断
常见简单请求 get post head
请求header里面无自定义头
Content-Type为以下几点
text/plain
multipart/from-data
application/x-www-form-urlencoded
非简单请求发送两道请求
–> 第一次发送OPTIONS方法的预检请求,用来向后台获取是否可以接受此非简单请求。
使用如下代码可告知浏览器可保存许可多长时间 单位秒 也就是说非简单请求浏览器不用每次都请求两次在允许时间类不用发生预检请求
1 | res.setHeader("Access-Control-Max-Age","3600"); |
在这里主要说一下 application/json的非简单请求
1 | $.ajax({ |
该请求发生的是json数据主要用于复合对象数据发送,在springmvc中接受这一类数据的形参要用@RequestBody注解表明,需要注意的是该方式发生的数据是”一个”json对象也就是最外层只能有一个对象,在对象内部可任意组合,但是要与后天接收参数结构一致,参数名一致