1.前端用ajax get访问接口,报错 : No 'Access-Control-Allow-Origin' header is present on the requested resource.
javascript处于安全考虑,不允许跨域访问,CORS导致的。
CORS(跨域资源共享,Cross-Origin Resource Sharing)是一种跨域访问的机制,可以让Ajax实现跨域访问。是一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其他源,使得浏览器允许这些域加载自己的资源。
Origin: https://bilibili.com # 表明该请求来自哪个源
在服务器的response header中,加入"Access-Control-Allow-Origin:*" 即可以支持CORS,apache/nginx等怎么配置,见参考文档。
举个例子:
* API部署在DomainA上;
* Ajax文件部署在DomainB上,Ajax文件会向API发送请求,返回数据;
* 用户通过DomainC访问DomainB的Ajax文件,请求数据
以上过程就发生了跨域访问。如果直接使用Ajax来请求就会失败,就像Chrome提示的:No 'Access-Control-Allow-Origin' header is present on the requested resource.
先打开百度首页,然后在浏览器console下直接使用fetch打开淘宝的首页:
fetch('https://www.taobao.com')
浏览器清楚这是一个跨域请求,用CORS机制的,所以浏览器会检查返回内容的头部,是否包含了 access control allow origin这个标头
切换到Network查看接口,可知这个例子中,访问淘宝的响应头中没有 access control allow origin,也就是不允许任何跨域的请求,因此,浏览器拒绝载入该内容。
Network中有一个请求:
同样在百度首页载入淘宝的页面,但这次请求加入定制的标头,并且内容改成了XML:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.taobao.com");
xhr.setRequestHeader("X-PINGOTHER", "pingpong");
xhr.setRequestHeader("Content-Type", "apptication/xml");
xhr.send("<person><name>Arun</name></person>");
Network中有两个请求:
得到了同样的CORS错误,在预检阶段失败了。
总结:
对于 简单请求,浏览器会直接请求服务端,然后检查返回内容头部的 access control allow origin,如果允许当前域,则正常的载入并展示返回内容,否则拒绝载入。
对于 非简单请求,浏览器会发出一个option方法,来询问服务器端, 是否允许来自当前域的请求访问该资源,服务器会返回允许访问的域HTTP方法以及允许的标头header,浏览器会进行判断后,决定是否发出真正的请求,这个询问的过程称为预检
设计后端请求时,将占用服务器过多资源的请求,通过预检可以降低服务器的负载,应该设计成非简单请求的服务。而轻量服务,则使用简单请求,提升响应速度。
# 简单请求请求
fetch('https://www.taobao.com')
# 预检请求,因为改动了请求头,不满足CORS简单请求的第二条要求
fetch('https://www.taobao.com', {
headers: {
a: 1,
},
});
# POST请求,简单请求
fetch('https://www.taobao.com', {
method: 'POST',
body: JSON.stringify({a: 1, b: 2}),
});
# 预检请求,因为改动了请求头content-type,不满足CORS简单请求的第三条要求
fetch('https://www.taobao.com', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({a: 1, b: 2}),
});
解决跨域问题有以下几种方法:
使用jsonp方法,需让服务端返回jsonp格式的response,如:Returning JSON/JSONP from a Django view with a little decorator help (Example)
使用CORS:这个用起来比较方便,现在大多数浏览器都支持了,且我web服务器完全开放给别人调用,所以比较推荐CORS。
使用Ajax获取json数据时,存在跨域的限制。不过,在Web页面上调用js的script脚本文件时却不受跨域的影响,JSONP就是利用这个来实现跨域的传输。因此,我们需要将Ajax调用中的dataType从JSON改为JSONP(相应的API也需要支持JSONP)格式。
JSONP只能用于GET请求。
pip install django-cors-headers
修改setting.py:
INSTALLED_APPS = [
'corsheaders' # 尽量放在前面
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware' # 需要添加在CommonMiddleware中间件前面(必须)
]
# 添加白名单:设置可以任意访问,默认为False
CORS_ORIGIN_ALLOW_ALL =True
# 也可以通过白名单列表配置允许跨域访问的地址
CORS_ORIGIN_WHITELIST = (
'localhost:3306',
'http://localhost:8080'
)
# CORS_ALLOW_CREDENTIALS允许跨域时携带Cookie,默认为False
CORS_ALLOW_CREDENTIALS = True
修改视图函数view.py,允许其他域通过Ajax请求数据:
def myview(_request):
response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
return response
location / {
# 跨域请求
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
# 发起预检请求,从而获知服务器是否允许该跨域请求
if ($request_method = 'OPTIONS') {
return 204;
}
}
总结一下预检请求通过的条件:
其他补充