外部 JSON-2 API¶
19.0 新版功能.
Odoo 通常通过模块在内部扩展,但其许多功能和所有数据也可在外部用于分析或与各种其他软件集成。部分 模型 API 通过 /json/2 端点可通过 HTTP 轻松访问。
小技巧
可用的实际模型、字段和方法特定于每个数据库,可以在其 /doc 页面上查阅。
注解
通过外部 API 访问数据仅适用于 自定义 Odoo 定价计划。外部 API 访问权限在 单应用免费版 或 标准版 计划中不可用。更多信息,请访问 Odoo 定价页面 或联系您的客户成功经理。
API¶
请求¶
在 /json/2/<model>/<method> URL 上发布 JSON 对象。
HTTP 头
- 主机
必需,服务器的主机名。
- 授权
必需,
bearer后跟一个 API 密钥 。- 内容类型
必需,
application/json,推荐指定字符集。- X-Odoo-数据库
可选,要连接的数据库名称。
- 用户代理
推荐,您的软件名称。
URL 路径
- 模型
必需,技术模型名称。
- 方法
必需,要执行的方法。
正文 JSON 对象
- ID
要在其上执行方法的记录 ID 数组。调用
@api.model装饰的方法时为空或省略。- 上下文
可选,附加值的对象。例如
{"lang": "en_US"}。- 参数
根据需要多次出现,方法的参数。
Example
POST /json/2/res.partner/search_read HTTP/1.1
Host: mycompany.example.com
X-Odoo-Database: mycompany
Authorization: bearer 6578616d706c65206a736f6e20617069206b6579
Content-Type: application/json; charset=utf-8
User-Agent: mysoftware python-requests/2.25.1
{
"context": {
"lang": "en_US"
},
"domain": [
["name", "ilike", "%deco%"],
["is_company", "=", true]
],
"fields": ["name"]
}
响应¶
成功 时,返回 200 状态码,正文中包含被调用方法的 JSON 序列化返回值。
Example
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{"id": 25, "name": "Deco Addict"}
]
错误 时,返回 4xx/5xx 状态码,正文中包含 JSON 序列化的错误对象。
- 名称
发生的 Python 异常的完全限定名称。
- 消息
异常消息,通常与
arguments[0]相同。- 参数
所有异常参数。
- 上下文
请求使用的上下文。
- 调试
异常回溯,用于调试目的。
Example
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{
"name": "werkzeug.exceptions.Unauthorized",
"message": "Invalid apikey",
"arguments": ["Invalid apikey", 401],
"context": {},
"debug": "Traceback (most recent call last):\n File \"/opt/Odoo/community/odoo/http.py\", line 2212, in _transactioning\n return service_model.retrying(func, env=self.env)\n File \"/opt/Odoo/community/odoo/service/model.py\", line 176, in retrying\n result = func()\n File \"/opt/Odoo/community/odoo/http.py\", line 2177, in _serve_ir_http\n self.registry['ir.http']._authenticate(rule.endpoint)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 274, in _authenticate\n cls._authenticate_explicit(auth)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 283, in _authenticate_explicit\n getattr(cls, f'_auth_method_{auth}')()\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 240, in _auth_method_bearer\n raise werkzeug.exceptions.Unauthorized(\nwerkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey\n"
}
Traceback (most recent call last):
File "/opt/Odoo/community/odoo/http.py", line 2212, in _transactioning
return service_model.retrying(func, env=self.env)
File "/opt/Odoo/community/odoo/service/model.py", line 176, in retrying
result = func()
File "/opt/Odoo/community/odoo/http.py", line 2177, in _serve_ir_http
self.registry['ir.http']._authenticate(rule.endpoint)
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 274, in _authenticate
cls._authenticate_explicit(auth)
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 283, in _authenticate_explicit
getattr(cls, f'_auth_method_{auth}')()
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 240, in _auth_method_bearer
raise werkzeug.exceptions.Unauthorized(
werkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey
配置¶
API 密钥¶
必须在 Authorization 请求头中设置 API 密钥,作为持有者令牌。
通过 为用户创建新的 API 密钥。
|
|
|
创建新的 API 密钥需要描述和持续时间。描述使得可以识别密钥,并在以后确定密钥是否仍在使用或应被移除。持续时间决定了密钥的生命周期,之后密钥将失效。对于交互式使用,建议设置较短的持续时间(通常为一天)。出于安全原因,无法创建持续时间超过三个月的密钥。这意味着长期使用的密钥必须至少每三个月轮换一次。
生成密钥 按钮会创建一个强大的 160 位随机密钥。密钥值仅在创建期间显示一次,以后无法检索。请立即复制密钥并安全存储。如果密钥泄露或丢失,请立即删除并生成新密钥。
请参考 OWASP 的秘密管理速查表 以获取有关 API 密钥管理的进一步指导。
访问权限¶
JSON-2 API 使用标准的 Odoo 安全模型 。所有操作都会根据用户的访问权限、记录规则和字段访问进行验证。
对于 交互式使用 ,例如探索 API 或运行一次性脚本,使用 个人账户 是可以的。
对于 扩展的自动化使用 ,例如与其他软件的集成,建议创建和使用 专用的机器人用户 。使用专用的机器人用户有几个好处:
可以授予机器人所需的最低权限,从而在 API 密钥泄露时限制影响。
可以将密码设置为空以禁用登录/密码认证,从而降低账户被泄露的可能性。
访问日志字段 使用机器人账户。没有用户被模拟。
数据库¶
根据部署情况,可能需要 Host 和/或 X-Odoo-Database 请求头。 Host 头是 HTTP/1.1 要求的,并且在 Odoo 与其他 Web 应用程序一起安装的服务器上是必需的,以便 Web 服务器/反向代理能够将请求路由到 Odoo 服务器。当单个 Odoo 服务器托管多个数据库且未配置 数据库过滤器 使用 Host 头时,需要 X-Odoo-Database 头。
大多数 HTTP 客户端库会自动使用连接 URL 设置 Host 头。
事务¶
所有对 JSON-2 端点的调用都在其自己的 SQL 事务中运行。事务在成功时提交,在错误时丢弃。使用 JSON-2 API,无法在单个事务中链接多个调用。这意味着在进行多个连续调用时必须小心,因为数据库可能会被其他并发事务修改。在执行与预订、付款等相关的操作时,这尤其危险。
解决方案是始终调用在单个事务中执行所有相关操作的单个方法。这样,数据保证保持一致:要么全部完成(成功,提交),要么什么都不做(错误,回滚)。
在 ORM 中, search_read 方法是在单个事务中执行多个操作(先 search 然后 read )的单个方法的示例。如果并发请求删除了 search 检索到的记录之一,则后续调用 read 可能会因缺少记录错误而失败。这样的问题不会发生在 search_read 中,因为系统保证了事务之间的适当隔离。
在业务模型中,这些方法通常以 action_ 为前缀,例如 sale.order 的 action_confirm 方法,该方法在确认销售订单之前验证其有效性。
当一组相关操作没有现有方法时,可以在专用模块中创建新方法。
代码示例¶
以下示例展示了如何在虚拟网站 https://mycompany.example.com 上托管的虚拟数据库 mycompany 上执行两个 常见 ORM 方法 。其 动态文档 将在 https://mycompany.example.com/doc 上提供。
import requests
BASE_URL = "https://mycompany.example.com/json/2"
API_KEY = ... # get it from a secure location
headers = {
"Authorization": f"bearer {API_KEY}",
"X-Odoo-Database": "mycompany",
"User-Agent": "mysoftware " + requests.utils.default_user_agent(),
}
res_search = requests.post(
f"{BASE_URL}/res.partner/search",
headers=headers,
json={
"context": {"lang": "en_US"},
"domain": [
("name", "ilike", "%deco%"),
("is_company", "=", True),
],
},
)
res_search.raise_for_status()
ids = res_search.json()
res_read = requests.post(
f"{BASE_URL}/res.partner/read",
headers=headers,
json={
"ids": ids,
"context": {"lang": "en_US"},
"fields": ["name"],
}
)
res_read.raise_for_status()
names = res_read.json()
print(names)
(async () => {
const BASE_URL = "https://mycompany.example.com/json/2";
const API_KEY = ; // get it from a secure location
const headers = {
"Content-Type": "application/json",
"Authorization": "bearer " + API_KEY,
"X-Odoo-Database": DATABASE,
}
const reqSearch = {
method: "POST",
headers: headers,
body: {
context: {lang: "en_US"},
domain: [
["name", "ilike", "%deco%"],
["is_company", "=", true],
],
},
};
const resSearch = await fetch(BASE_URL + "/res.partner/search_read", reqSearch);
if (!response.ok) throw new Error(resSearch.json());
const ids = await resSearch.json();
const reqRead = {
method: "POST",
headers: headers,
body: {
ids: ids,
context: {lang: "en_US"},
fields: ["name"],
},
};
const resRead = await fetch(BASE_URL + "/res.partner/search_read", reqRead);
if (!response.ok) throw new Error(resRead.json());
const names = await resRead.json();
console.log(names);
})();
set -eu
DATABASE=mycompany
BASE_URL=https://$DATABASE.odoo.com/json/2
API_KEY=
ids=$(curl $BASE_URL/res.partner/search \
-X POST \
--oauth2-bearer $API_KEY \
-H "X-Odoo-Database: $DATABASE" \
-H "Content-Type: application/json" \
-d '{"context": {"lang": "en_US"}, "domain": [["name", "ilike", "%deco%"], ["is_company", "=", true]]}' \
--silent \
--fail
)
curl $BASE_URL/res.partner/read \
-X POST \
--oauth2-bearer $API_KEY \
-H "X-Odoo-Database: $DATABASE" \
-H "Content-Type: application/json" \
-d "{\"ids\": $ids, \"context\": {\"lang\": \"en_US\"}, \"fields\": [\"name\"]}" \
--silent \
--fail-with-body
上述示例等同于运行::
Model = self.env["res.partner"].with_context({"lang": "en_US"})
records = Model.search([("name", "ilike", "%deco%"), ("is_company", "=", True)])
return json.dumps(records.ids)
然后,在新事务中::
records = self.env["res.partner"].with_context({"lang": "en_US"}).browse(ids)
names = records.read(["name"])
return json.dumps(names)
动态文档¶
正在建设中
从 XML-RPC / JSON-RPC 迁移¶
端点 /xmlrpc 、 /xmlrpc/2 和 /jsonrpc 上的 XML-RPC 和 JSON-RPC API 都计划在 Odoo 20(2026 年秋季)中移除。这两个 RPC API 都公开了三个相同的服务:common、db(数据库)和 object。所有三个服务都已弃用。
注解
其他控制器 @route(type='jsonrpc') (在 Odoo 18 之前称为 type='json' )不受此弃用通知的影响。
通用服务¶
通用服务定义了 3 个函数:
version()login(db, login, password)authenticate(db, login, password, user_agent_env)
version 函数被 /web/version 端点取代。
GET /web/version HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{"version_info": [19, 0, 0, "final", 0, ""], "version": "19.0"}
login 和 authenticate 这两个函数在成功登录后返回相应用户的用户 ID。用户 ID 和密码对于后续对 object 服务的 RPC 调用是必需的。JSON-2 API 使用不同的身份验证方案,其中既不使用用户 ID 也不使用密码。仍然可以通过向 res.users/context_get 发送不带 ID 的 JSON-2 请求来检索用户自己的 ID(当前用户从 API 密钥中提取)。
数据库服务¶
数据库服务定义了 13 个函数:
create_database(master_pwd, db_name, demo, lang, user_password, login, country_code, phone)duplicate_database(master_pwd, db_original_name, db_name, neutralize_database)drop(master_pwd, db_name)dump(master_pwd, db_name, format)restore(master_pwd, db_name, data, copy)change_admin_password(master_pwd, new_password)rename(master_pwd, old_name, new_name)migrate_databases(master_pwd, databases)db_exist(db_name)list()list_lang()list_countries(master_pwd)server_version()
这些函数中的许多可以通过 /web/database 控制器访问。这些控制器与 /web/database/manager 上的 HTML 表单协同工作,并且可以通过 HTTP 访问。
以下控制器使用动词 POST 和内容类型 application/x-www-form-urlencoded 。
/web/database/create接受输入master_pwd、name、login、password、demo、lang和phone。/web/database/duplicate接受输入master_pwd、name、new_name和neutralize_database(默认不中和)。/web/database/drop接受输入master_pwd和name。/web/database/backup接受输入master_pwd、name和backup_format(默认为 zip),并在 http 响应中返回备份。/web/database/change_password接受输入master_pwd和master_pwd_new。
以下控制器使用动词 POST 和内容类型 multipart/form-data 。
/web/database/restore接受输入master_pwd、name、copy(默认不复制)和neutralize(默认不中和),它接受文件输入backup_file。
以下控制器使用动词 POST 和内容类型 application/json-rpc 。
/web/database/list接受一个空的 JSON 对象作为输入,并在 JSON 响应的result条目下返回数据库列表。
剩余的函数是: server_version ,存在于 /web/version 下; list_lang 和 list_countries ,通过 JSON-2 存在于 res.lang 和 res.country 模型上;以及 migrate_databases ,目前作为非可编程 API。
对象服务¶
对象服务定义了 2 个函数:
execute(db, uid, passwd, model, method, *args)execute_kw(db, uid, passwd, model, method, args, kw={})
它们都提供对所有公共模型方法的访问,包括通用的 ORM 方法。
这两个函数都是无状态的。这意味着每次调用都需要提供数据库、用户 ID 和用户密码。还必须提供模型、方法和参数。 execute 函数接受尽可能多的额外位置参数。 execute_kw 函数接受一个位置参数的 args 列表和一个可选的 kw 关键字参数字典。
记录 ID 从第一个 args 中提取。当调用的方法用 @api.model 装饰时,不会提取记录 ID,并且 args 保持原样。只有使用 execute_kw 才能提供上下文,因为它从名为 context 的关键字参数中提取。
Example
要运行以下内容:
(env['res.partner']
.with_user(2) # admin
.with_context(lang='en_US')
.browse([1, 2, 3])
.read(['name'], load=None)
)
使用 XML-RPC(JSON-RPC 类似):
from xmlrpc.client import ServerProxy
object = ServerProxy(...)
ids = [1, 2, 3]
fields = ['name']
load = None
object.execute("database", 2, "admin", "res.partner", "read", ids, fields, load)
object.execute("database", 2, "admin", "res.partner", "search", [
ids,
fields,
], {
"context": {"lang": "en_US"},
"load": load,
})
JSON-2 API 取代了对象服务,但有一些不同。数据库仅在同一个域有多个数据库可用的系统上必须提供(通过 X-Odoo-Database HTTP 头)。登录/密码身份验证方案被 API 密钥取代(通过 Authorization: bearer HTTP 头)。 model 和 method 放在 URL 中。请求体是一个 JSON 对象,包含所有方法的参数,加上 ids 和 context 。所有参数都是命名的;在 JSON-2 中无法使用位置参数调用函数。
Example
使用 JSON-2:
import requests
DATABSE = ...
DOMAIN = ...
API_KEY = "6578616d706c65206a736f6e20617069206b6579"
requests.post(
f"https://{DOMAIN}/json/2/res.partner/read",
headers={
# "X-Odoo-Database": DATABASE, # only when DOMAIN isn't enough
"Authorization": f"bearer {API_KEY}",
},
json={
"ids": [1, 2, 3],
"context": {"lang": "en_US"},
"fields": ["name"],
"load": None,
},
).json()