React Native通过Axios调用Odoo search_read接口遇CORS报错
问题背景
使用React Native技术栈,通过Axios调用本地部署Odoo实例的API接口。
已成功调通/web/session/authenticate认证接口,实现代码如下:
const authenticate = await axios.post('http://localhost:8069/web/session/authenticate', { params: { db: 'db', login: 'odoo', password: 'odoo', } } );
完成认证后调用/web/dataset/search_read接口拉取业务数据,实现代码如下:
const search_read = await axios.post('http://localhost:8069/web/dataset/search_read', { params: { model: 'stock.picking', fields: ['id','name'], } } );
调用时触发CORS拦截错误:
源地址
http://localhost:19006发起的指向http://localhost:8069/web/dataset/search_read的XMLHttpRequest请求已被CORS策略拦截:被请求资源未返回Access-Control-Allow-Origin响应头。
已尝试通过继承Odoo控制器的方式,为两个接口添加cors='*'配置,对应代码如下:
from odoo.addons.web.controllers.main import Session, DataSet from odoo import http class SessionInherit(Session): @http.route('/web/session/authenticate', type='json', auth="none", cors='*') def authenticate(self, db, login, password, base_location=None): return super(SessionInherit, self).authenticate(db, login, password, base_location) class DataSetInherit(DataSet): @http.route('/web/dataset/search_read', type='json', auth="user", cors='*') def search_read(self, model, fields=False, offset=0, limit=False, domain=None, sort=None): return super(DataSetInherit, self).search_read(model, fields, offset, limit, domain, sort)
其中authenticate接口添加配置后CORS问题解决,但search_read接口添加相同配置后仍触发拦截。
根因分析
三个核心问题导致配置不生效:
- 跨域请求默认不携带Cookie:
authenticate接口认证类型为auth="none",不需要鉴权就能访问,所以加CORS头后直接正常返回。但search_read是auth="user"类型,需要携带合法登录session才能访问。Axios默认不会在跨域请求中携带Cookie,导致Odoo判定请求未登录,直接由框架层返回302重定向到登录页,这个重定向响应不会经过重写的控制器方法,自然不会带上配置的CORS头,触发拦截。 - 带凭证的跨域请求不支持通配符CORS配置:就算请求带上了Cookie,
cors='*'的配置在携带凭证的跨域场景下会被浏览器直接拒绝,不符合CORS规范。 - 跨域请求默认不会携带CSRF Token,Odoo默认开启CSRF校验,就算跨域和session都正常,也会被403拦截。
解决方案
按以下步骤修改即可解决问题:
1. 修改Axios配置,允许跨域携带凭证
在请求配置中开启withCredentials,让跨域请求自动携带对应域名下的Cookie:
// 方案1:全局配置,对所有Axios请求生效 axios.defaults.withCredentials = true; // 方案2:单独为search_read请求配置 const search_read = await axios.post('http://localhost:8069/web/dataset/search_read', { params: { model: 'stock.picking', fields: ['id','name'], } }, { withCredentials: true } );
2. 修正Odoo侧控制器配置
- 将CORS配置从通配符
*改为前端实际源地址http://localhost:19006,符合带凭证跨域的规范要求 - 为
search_read接口添加csrf=False关闭CSRF校验(跨域场景下无法自动携带CSRF Token)
修改后的代码如下:
from odoo.addons.web.controllers.main import Session, DataSet from odoo import http class SessionInherit(Session): @http.route('/web/session/authenticate', type='json', auth="none", cors='http://localhost:19006') def authenticate(self, db, login, password, base_location=None): return super(SessionInherit, self).authenticate(db, login, password, base_location) class DataSetInherit(DataSet): @http.route('/web/dataset/search_read', type='json', auth="user", cors='http://localhost:19006', csrf=False) def search_read(self, model, fields=False, offset=0, limit=False, domain=None, sort=None): return super(DataSetInherit, self).search_read(model, fields, offset, limit, domain, sort)
3. 生效操作
修改完Odoo代码后,需要进入应用列表找到你的自定义模块,点击升级,之后重启Odoo服务,清除浏览器缓存再测试即可。
排查指引
如果修改后仍有问题,按以下顺序排查:
- 打开浏览器开发者工具的Network面板,查看
search_read请求的响应状态码:- 如果是302:说明session未正确携带,检查Axios的
withCredentials配置是否开启 - 如果是403:检查控制器是否加了
csrf=False,自定义模块是否升级成功 - 如果是200但仍报CORS错误:检查自定义模块的
__manifest__.py中depends列表是否添加了web依赖,路由重写是否生效
- 如果是302:说明session未正确携带,检查Axios的
- 确认Odoo服务没有在前面加反向代理(如Nginx),如果有反向代理需要同步在代理层配置CORS头。
内容的提问来源于stack exchange,提问作者holydragon
相关产品推荐
相关产品推荐

