【四】Ajax与异步编程之数据传输格式ajax同源策略和跨域方案
【1】同源策略
- 同源策略(ame origin policy),是浏览器为了保护用户信息在服务端的安全的而设置一种安全机制。
- 所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器客户端页面地址)之间比较,是否协议、域名(IP)和端口相同。
- 不同源的客户端脚本[javascript]在没有服务器明确授权的情况下,没有权限读写服务端的信息。
- ajax本质上还是javascript,是运行在浏览器中的脚本语言,所以会被受到浏览器的同源策略所限制。
前端地址:http://www.fuguang.cn/index.html |
是否同源 | 原因 |
---|---|---|
http://www.fuguang.cn/user/login.html |
是 | 协议、域名、端口相同 |
http://www.fuguang.cn/about.html |
是 | 协议、域名、端口相同 |
https://www.fuguang.cn/user/login.html |
否 | 协议不同 ( https和http ),端口不同 |
http:/www.fuguang.cn:5000/user/login.html |
否 | 端口不同( 5000和80) |
http://bbs.fuguang.cn/user/login.html |
否 | 域名不同 ( bbs和www ) |
同源策略针对ajax的拦截
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="music" value="月亮之上">
<button class="btn">搜索歌曲</button>
<script>
let btn = document.querySelector(".btn")
let music = document.querySelector("input[name=music]")
btn.onclick = function(){
fetch(`http://msearchcdn.kugou.com/api/v3/search/song?keyword=${music.value}`,{
method: "get",
}).then(response=>response.json()).then(response=>{
console.log(response)
}).catch(error=>{
console.log(error)
})
}
</script>
</body>
</html>
效果:
【2】ajax跨域(跨源)方案
-
因为同源策略的问题,所以我们一般情况下只要使用了ajax进行服务端的数据的操作都会面临着同源策略的拦截和影响。
- 一般在开发中解决这个问题,我们也叫ajax的跨域(跨源)问题。
-
所谓的跨域实际是就是让我们的javascript代码可以在基于与服务端不同源的客户端下正常运行。
-
一般出现跨域问题时,代码在浏览器的console终端下,都会显示如下错误信息:
当然,上面的错误有时候并非一定就是同源策略的问题,也有可能是因为以下原因:
1. 服务端代码报错,或者服务端没有正确的返回授权
2. 客户端请求的服务端的请求报文出错[错误的url地址,错误的请求方法,错误的请求头]
3. 客户端携带了不明来历的cookie信息
在工作中,跨域的解决方案一般无非以下三种:
-
cors方案
-
W3C维护的一个跨域资源共享的协议。
-
这个协议要求服务端通过响应头的方式告知浏览器,当前跨域访问的客户端是否属于可信任客户端。
-
cors方案的使用前提是服务端已经支持了cors。
-
-
服务端代理(Proxy Server)
- 自己搭建一个可控的代理服务端(自己的服务端项目),因为同源策略只存在于浏览器,所以我们让客户端直接请求代理服务端,然后代理服务端接收客户端请求以后,执行代码请求目标服务端那边获取到客户端需要的数据,最终由代理服务端把数据返回给客户端。
-
jsonp方案
- 原则上来说jsonp不是ajax技术,而是利用了HTML中的某些默认能跨域的标签来实现的。
- script,link,src属性,iframe,…
客户端先预定义一个js函数。
然后服务端生成js代码,代码里面把数据作为函数的参数,在客户端请求的时候返回。
(1)cors方案和服务端代理
客户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="music" value="月亮之上">
<button class="btn">搜索歌曲</button>
<script>
let btn = document.querySelector(".btn")
let music = document.querySelector("input[name=music]")
btn.onclick = function(){
fetch(`http://127.0.0.1:8000/?music=${music.value}`,{
method: "get",
}).then(response=>response.json()).then(response=>{
console.log(response)
}).catch(error=>{
console.log(error)
})
}
</script>
</body>
</html>
代理服务端代码:
import json
from aiohttp import web, ClientSession
import aiohttp_cors
routes = web.RouteTableDef()
@routes.get("/")
async def get_index(request):
# 返回json数据
music = request.query.get("music", None)
if music is None:
return web.json_response({"errno": -1, "errmsg": "参数有误!"})
# 发起请求,搜索音乐
# http://msearchcdn.kugou.com/api/v3/search/song?keyword=
url = f"http://msearchcdn.kugou.com/api/v3/search/song?keyword={music}"
async with ClientSession() as session:
async with session.get(url) as response:
print("status:{}".format(response.status))
if response.status == 200:
data = await response.text()
data = json.loads(data)
print(data)
return web.json_response(data)
else:
return web.json_response({"errno": -1, "errmsg": "搜索失败!"})
class Application(web.Application):
def __init__(self, routes):
"""
:param routes: url和服务端处理程序之间的绑定关系, 这种绑定关系,我们一般称之为路由,当然,在服务端开发中,我们也会经常把具有路由功能功能的类/对象,称之为"路由类"或"路由对象"
"""
print(routes._items) # 路由列表
# 先让官方的aiohttp的web框架先进行初始化
super(Application, self).__init__()
# 注册路由信息到web框架中,routes是一个list列表
self.add_routes(routes)
# 这里内部完成了一个for循环,把routes里面每一个路由信息都了遍历,添加一个返回值用于实现跨域资源共享[CORS]。
cors = aiohttp_cors.setup(self, defaults={
"*": aiohttp_cors.ResourceOptions(
allow_credentials=True,
expose_headers="*",
allow_headers="*",
)
})
# Configure CORS on all routes.
for route in list(self.router.routes()):
cors.add(route)
if __name__ == '__main__':
web.run_app(Application(routes),host="0.0.0.0",port=8000)
(2)jsonp方案
客户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="username" value="小明">
<button class="btn">发送数据</button>
<script>
var btn = document.querySelector('.btn')
var user = document.querySelector('input[name="username"]')
btn.onclick = function(){
// <script src="http://127.0.0.1:8000/1.js?username=xiaoming&func=callback"><//script>
let script = document.createElement("script")
script.src= `http://127.0.0.1:8000/search?username=${user.value}&func=callback`
document.head.append(script)
}
function callback(data){
console.log(data);
}
</script>
</body>
</html>
服务端代码:
import json
from aiohttp import web
routes = web.RouteTableDef()
@routes.get("/search")
async def get_js(request):
# 接受客户端上传过来的请求体数据,并使用json.loads()转换成字典
username = request.query.get("username")
func = request.query.get("func", "callback")
# 假设根据username到数据库中查询数据
data = {
"id": 1,
"username": username,
"age": 20
}
return web.Response(text=f"{func}({json.dumps(data)})", content_type="text/javascript")
class Application(web.Application):
def __init__(self, routes):
"""
:param routes: url和服务端处理程序之间的绑定关系, 这种绑定关系,我们一般称之为路由,当然,在服务端开发中,我们也会经常把具有路由功能功能的类/对象,称之为"路由类"或"路由对象"
"""
print(routes._items) # 路由列表
# 先让官方的aiohttp的web框架先进行初始化
super(Application, self).__init__()
# 注册路由信息到web框架中,routes是一个list列表
self.add_routes(routes)
if __name__ == '__main__':
web.run_app(Application(routes),host="0.0.0.0",port=8000)
(3)总结:
1. 同源策略:浏览器的一种保护用户数据的一种安全机制。
浏览器会限制脚本语法不能跨源访问其他源的数据地址。
同源:判断两个通信的地址之间,是否协议,域名[IP],端口一致。
ajax: http://127.0.0.1/index.html
api数据接口: http://localhost/index
这两个是同源么?不是同源的。
2. ajax默认情况下会受到同源策略的影响,一旦受到影响会报错误如下:
No 'Access-Control-Allow-Origin' header is present on the requested resource
3. 解决ajax只能同源访问数据接口的方式有3种,比较常用的是cors.
在服务端的响应头中设置:
Access-Control-Allow-Origin: 允许访问的域名地址
标签:web,跨域,Ajax,ajax,同源,routes,response,服务端,客户端
From: https://www.cnblogs.com/dream-ze/p/17557954.html