这波跨域不亏
遇到一个跨域问题,说到这里大家就心里默然一笑了,因为不外乎就那样的balabala,但这次有点不一样的在于:后端确定自己已经开启了跨域!这...就有点尴尬了
于是问题变成,我是怎么判断跨域的?
首先是最简单的http://10.63.231.79:8080
向http://47.106.111.100:8088
发post请求,无疑已经跨域了,
其次,因为我的是vue-cli
搭建的(我负责webview
部分),然后用的axios
发请求,按api地址来发数据的时候,报错403
,如下图:
看起来,我很有道理,但是有两个问题
第一,为什么postman
可以成功呢?
第二,为什么安卓端的请求也可以成功呢?
安卓端的请求如下:
第一个问题
通过为什么使用postman发送请求时不会有跨域问题?得知,
准确的跨域定义:当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求,通俗一点,正常的跨域情况,是你访问了一个A网站,然后这个网站返回的资源里面,请求了B网站/端口的资源,于是就跨域了
跨域这个情况只会出现在浏览器页面里,因为实际上是浏览器由于安全原因限制了这些请求的访问。在postman里面,实际上每发出一个请求,都是在独立请求一个资源
还有很解惑的一句话:
应该是POSTMAN中有个类似java程序或者node 代理了跨域接口,先用node去请求跨域接口,得到数据,然后本地发起ajax请求,实际请求的是node代理过后的接口,就不存在跨域了
只有在浏览器端发 ajax 请求才会存在跨域
http请求不会存在跨域
先不说正确性,其中的那句,实际请求的是node代理过后的接口,立马联想到,这应该也是设置代理能解决跨域的原因吧(将ajax请求交给代理变成由代理发出http请求),也就是句子最后的,只有在浏览器端发ajax请求才会存在跨域,http请求不会存在跨域
第二个问题
的确不明白,但似乎也是这样的理由吧?
后询问安卓的同学是使用的okhttp
,上面赫然写着这样一句话:
OkHttp is an HTTP client
这不就差不多又是一个postman
吗...似乎到这里事情已经很明白了,但我们需要再看一下的是
浏览器的报错信息:
Response to preflight request doesn't pass access control check: No'Access-Control-Allow-Origin'
header is present on the requested resource. Origin'http://10.63.231.79:8080'
is therefore not allowed access. The response had HTTP status code 403.
什么是preflight request
?
参考链接: HTTP访问控制(CORS)-MDN
规范要求,对那些可能对服务器数据产生副作用的
HTTP
请求方法(特别是GET 以外的 HTTP请求,或者搭配某些MIME
类型的POST
请求),浏览器必须首先使用OPTIONS
方法发起一个预检请求(preflight request
),从而获知服务端是否允许该跨域请求"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
简单请求
另联想到,遇到的axios跨域(也就是上述的报错),搜索到的axios可以解决跨域访问的问题吗?,说道:
当server
端不支持跨域,如果不是自己开发的,那么可以自己写个后端转发该请求,用代理的方式实现。(后来的自己就是这样解决跨域的)
当server
端支持跨域,但不能响应OPTIONS
请求时,如果 server
端也支持简单请求(见下方定义),特别是 axios POST
请求时,默认使用 JSON
格式,改成 string
问题就解决了!Using application/x-www-form-urlencoded format,需要转成application/x-www-form-urlencoded
,可以用qs.stringify(data)
,那么这里又是为什么可以呢?
重点就在于简单请求,查看上述MDN的介绍:
某些请求不会触发
CORS
预检请求。本文称这样的请求为“简单请求”若请求满足所有下述条件,则该请求可视为“简单请求”:
其中条件有(还有其他,请看文档):
Content-Type
的值仅限于下列三者之一:text/plain
,multipart/form-data
,application/x-www-form-urlencoded
原来这个原理就是,把跨域的请求,通过转换成简单请求,这样就不会触发了OPTIONS
了,但我的确这样做了之后,发送的预检请求报200了,但是结果控制台还是报跨域的错...这?
注意:这些跨域请求与浏览器发出的其他跨域请求并无二致。如果服务器未返回正确的响应首部,则请求方不会收到任何数据。因此,那些不允许跨域请求的网站无需为这一新的 HTTP 访问控制特性担心。
额...原来白高兴一场...
通过响应头部来判断是否开跨域
后续再看MDN文档时更找到一个有力的证据判断服务器是否开跨域:
假如站点 http://foo.example
的网页应用想要访问 http://bar.other
的资源,请求报文和响应报文:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
请求首部字段 Origin
表明该请求来源于 http://foo.exmaple
,响应中携带了响应首部字段 Access-Control-Allow-Origin
。使用Origin
和 Access-Control-Allow-Origin
就能完成最简单的访问控制
Access-Control-Allow-Origin: *
表明,该资源可以被任意外域访问
Access-Control-Allow-Origin: http://foo.example
表明,仅允许来自 http://foo.example
的访问
这就指明了一个更强的方法判断服务器开没开跨域:在响应中,查看是否有Access-Control-Allow-Origin
字段
想到怎么测试呢? 用github
的api
啊:
可以看到github
的api
完全支持跨域
事至此,可以看到这个证明题基本已经差不多了,那就是:服务器没开跨域!
接着就是,你是怎么解决的跨域?
首先一开始由于不确定是不是跨域的问题,因此换了三种发ajax请求的框架,axios
,vue-resource
(重识vue-resource ),jquery
,但结果其实都会报错,
其中用jquery
发post
请求到后端的时候,用jsonp
来发post
请求,来解决跨域:
$.ajax('http://47.106.111.100:8088/rest/app/register', {
type: "POST",
data: test,
dataType: 'jsonp',
crossDomain: true,
success: function (data) {
alert('hello');
if (data && data.resultcode == '200') {
console.log(data.result.today);
}
}
});
还是会出现Cross-Origin Read Blocking (CORB) blocked cross-origin response
那么这是为什么呢?
首先定义jsonp
: 创建一个 script
标签,将 src
设置为目标请求,插入到 dom
中,服务器接受该请求并返回数据,数据通常被包裹在 回调钩子
中
可以用
jsonp
发送post
请求么?显然不行,看过支持 post 请求的 script 么?
详细解释:
Response to preflight request doesn't pass access control check
总结:
也就是jsonp:
- 不支持
post
请求 - 需要后台配合(You need the server to return data in JSONP format too)
跨域的解决有:
If you do NOT want to:
- Disable web security in Chrome
- Use JSONP
- Use a third party site to re-route your requests
或者
You are running into CORS issues.There are several ways to fix this.
- Turn off CORS. For example: how to turn off cors in chrome
- Use a plugin for your browser
- Use a proxy such as nginx. example of how to set up
于是凉凉...,post
请求肯定触发跨域,而jsonp
是不行了,联想到之前有过印象,webpack
可设置代理来解决跨域问题(大概是上面postman提到的那种思想),于是查一下,get!
webpack 前后端分离开发接口调试解决方案,proxyTable解决方案
在config/index.js
里面找到proxyTable
proxyTable: {
"/rest": "http://47.106.111.100:8088"
},
这样的话,请求到 /rest/app/register
,现在会被代理到请求 http://47.106.111.100:8088/rest/app/register
。 更多的用法查看上面的http-proxy-middleware
文档即可
就是这样,all is done !