跨域这个问题其实前端很容易碰到,为什么呢?因为前端一直用浏览器啊,浏览器可以看做一个http客户端,前端和http打交道主要用的就是这个客户端,但如果后端和http打交道,一般只能使用curl这类工具,又因为浏览器的同源策略才产生了跨域,换句话说跨域的时候,后端对前端的数据进行了处理,并返回了对应的数据,但是浏览器检测到了跨域,所以把数据屏蔽了,这才产生了跨域,所以前端相比后端会更容易碰到跨域问题。那前端就一定会碰到跨域问题吗,并不是这样。比如之前我在做itbasic的时候一直没有碰到这个问题,那是为什么呢?我的前端脚本和后端api服务都在80这个端口下,也就是说域是一直一样的,所以不会产生,但在当下,前后端分离,很多时候,前后端在不同的web服务下,比如我们那个bbs,前端静态页面由apache提供,端口8080,后端api是swoole提供,端口56735,这样前端肯定就遇到跨域问题啦。
关于浏览器同源策略的原因,可以网上查看各种资料,阮一峰的博客是个很好的选择。
什么时候会出现跨域
1 | http://www.example.com/dir2/other.html:同源 |
如果解决跨域呢,自己主要了解的有两种,分别是cors和jsonp,自己平时用的主要是cors。
cors可以让后端代码不用做过多的变动,只需要对返回的内容多添加些头部信息即可。
jsonp主要是通过script,img标签这类不存在跨域,回忆一下,是不是通过img加载过百度的图片,通过script的src加载过cdn上的jquery。
通过script返回的内容因为在script标签内部,所以可以执行script代码,所以当我们传给后端我们定义好的方法名,然后让他们把我们需要的数据放进去,再返回来,类似
1 | showData({"1":"boy"}); |
上面的返回结果在script代码中自动执行,就能得到我们想要的结果(需要注意的是传入的数据必须是json类型哦,毕竟后端和js是不同的语言类型,然后我们只需要在showData里面对json数据进行转换成js能识别的数据即可)。
对于jsonp,我用的比较少,因为很多时候不想特别写专门用来跨域的api接口,所以如果要用jsonp,后端逻辑代码需要更改(百度php如何配合jsnop完成数据传输)。
对于前端,像jquery有对jsonp的封装,可以自定义函数,然后传入,返回的数据会先走定义的函数,然后来到success函数里面,注意success传入的参数data是不包括后端返回的内容中的函数,而是函数的参数们。
juqery默认传的自定义函数名称是jquery带一长串数字,具体的用法可以百度查看,我没咋用过。
还有哦,jsonp只能支持get请求。
重点
鉴于上面诸多的麻烦,我用的主要是cors。
首先我们需要知道浏览器将跨域请求分成了简单请求和非简单请求,什么是简单请求,
http 方法:
head, get,post
http 头部:
Accept,Accept-Language,Content-Lanuage,Last-Event-ID
Content-type 仅能是下列之一:
application/x-www-form-urlencoded,multipart/form-data,text/plain
不是简单请求,那就是非简单请求
大部分情况下我们的请求都是非简单请求,因为很多时候我们需要的content-type application/json。
对于非简单请求,会先在正式请求之前发送一个options请求,当有跨域请求的时候,我们可以看下Chrome的控制面板,很清楚的能看到。对于返回信息,我们需要加以下头信息
1 | $response->header("Access-Control-Allow-Origin", '*'); //允许的ip |
notice:
options 请求只是个探针请求,我们并不需要他执行业务处理,他的功能只是获取我们response中的一些头部信息,而且因为他有些信息不回携带,别入header 中不会有Authorizon 这个属性,所以很多业务他也走不通,比如对于Authorizon 中数据的验证,当options 请求成功后,后面才会继续发送真正的请求,这时候我们才是真正的逻辑处理,所以很多时候web server 比如nginx 直接过滤掉了options 请求,我们在swoole 中这么处理
1 | if ($request->server['request_method'] == 'OPTIONS') { |
其实上面那样设置header 中允许的请求域并不是太好,没有太多的安全限制,一般可以给个白名单,而且还有个问题就是如果设置成* 并不能允许携带cookie
1 | 客户端: |
更多详细的cors 跨域参考这篇文章
大致只需要做这些处理,一般像php,只需要在router的 end(返回具体信息)之前加上上面的信息就可以了。
像node,或者swoole都在启动web服务的文件处加上即可。