最近在工作中做到了一些前端开发工作,由于工作环境受限,只能使用原生的js,在开发过程中遇到了跨域请求问题,特此记录。
一、为什么会出现跨域问题
本质上是浏览器有同源策略限制。同源策略(Same Origin Policy) 是一种约定,是浏览器最核心的安全功能,如果缺少了同源策略,浏览器的正常功能可能会受到影响。可以说,web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略会阻止一个域的js脚本和另一个域的内容进行交互,所谓同源就是同一个域,即两个页面具有相同的协议、主机和端口号。
二、什么是跨域
当一个请求url的协议、域名和端口号三者之间任意一个与当前页面url不同,即为跨域。
当前页面url | 请求url | 是否跨域 | 原因 |
---|---|---|---|
http://www.current.com | http://www.request.com/index.html | 否 | 协议、域名、端口号相同 |
http://www.current.com | https://www.request.com/index.html | 是 | 协议不同(http/https) |
http://www.current.com | http://www.baidu.com | 是 | 域名不同 |
http://www.current.com | http://current.com | 是 | 主域名不同 |
http://www.current.com:8080 | http://www.current.com:8081 | 是 | 端口号不同 |
我遇到的问题是无法使用js向后端发起http请求:
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"model": "modelscope.cn/Qwen/Qwen2.5-3B-Instruct-GGUF:latest",
"messages": [
{"role": "user", "content": message}
],
"stream": false
})
});
如代码所示,我需要调用云上部署的阿里通义千问大模型的api,在开发前端界面过程中出现了阻止跨域访问的问题,跨域原因是域名不同(本机与云服务器不域名不同)。
三、解决办法
跨域问题通常要在后端解决,前端虽然有一些可以跳过跨域检查的方法,但是存在安全问题,不推荐使用。
http.server
以原生的Python中requests库为例,在服务器收到请求之后,会执行预检操作,直接在预检请求中设置跨域请求头:
from http.server import BaseHTTPRequestHandler, HTTPServer
class CORSRequestHandler(BaseHTTPRequestHandler):
def do_OPTIONS(self):
"""处理 OPTIONS 方法的预检请求"""
self.send_response(200, "ok")
# 添加 CORS 头
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
self.send_header('Access-Control-Max-Age', '86400') # 24小时缓存
self.end_headers()
def do_GET(self):
"""处理 GET 请求时也需要包含 CORS 头"""
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
response = {"message": "Hello, CORS!"}
self.wfile.write(bytes(str(response), "utf-8"))
def run(server_class=HTTPServer, handler_class=CORSRequestHandler, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print(f'Starting httpd server on port {port}...')
httpd.serve_forever()
if __name__ == "__main__":
run()
这样就可以实现跨域访问,但是注意,在开发环境中可以这样去写,方便调试和测试,但是在生产环境中,尽量不要使用这种写法:
self.send_header('Access-Control-Allow-Origin', '*')
Flask
直接安装flask_cors
库,然后通过以下方式可以直接接收跨域请求:
from flask import Flask, request, jsonify, make_response
from flask_cors import CORS # 导入CORS
app = Flask(__name__)
CORS(app) # 启用CORS,允许所有路由接受跨域请求
这种方式比较简单。