API /json/2 của Odoo thay xác thực bằng mật khẩu bằng API key dạng bearer và cuối cùng cũng trả về mã lỗi HTTP thật. Có gì thay đổi, viết lại lời gọi ra sao, và hạn gỡ bỏ.
Bài này nói về API /json/2 mới là gì, nó khác với những thứ trước đó ra sao, cách tạo API key, và cách viết lại các khuôn mẫu tích hợp phổ biến nhất.
Vì sao API cũ sắp bị bỏ
Các endpoint XML-RPC và JSON-RPC đã là API đối ngoại của Odoo từ những ngày đầu. Chúng chạy được, nhưng mang theo nhiều năm quyết định thiết kế vốn hợp lý vào thời điểm đó và nay đã thành vướng víu.
Có hai vấn đề nổi lên trên thực tế.
Thứ nhất là xác thực. Cả hai API đều xác thực bằng tên đăng nhập và mật khẩu. Với các tích hợp máy nói chuyện với máy, điều đó nghĩa là phải lưu thông tin đăng nhập của một người dùng ở đâu đó, phải đổi lại mỗi khi người đó đổi mật khẩu, và phải chấp nhận rằng một thông tin đăng nhập bị lộ là trao trọn quyền ở mức người dùng. API key là cách giải quyết tiêu chuẩn cho vấn đề này, nhưng API cũ chưa bao giờ dùng tới.
Thứ hai là cách báo lỗi. Cả endpoint XML-RPC lẫn JSON-RPC đều trả về HTTP 200 khi có lỗi. Thông tin lỗi bị chôn trong phần thân phản hồi. Điều đó làm hỏng mọi công cụ giám sát HTTP tiêu chuẩn, mọi thư viện thử lại, và trực giác của mọi lập trình viên về ý nghĩa của một mã 200. Bạn phải phân tích từng phản hồi mới biết được lời gọi thành công hay không.
/json/2 sửa cả hai.
/json/2 hoạt động ra sao
Cấu trúc rất thẳng thắn: POST /json/2/<model>/<method>.
Model là tên kỹ thuật của model (res.partner, sale.order). Method là thứ bạn đang gọi (search, read, search_read, create, write, unlink, hoặc bất kỳ phương thức tùy chỉnh nào được phơi ra bên ngoài). Phần thân request là một đối tượng JSON chứa các tham số của phương thức theo tên.
Header của request
| Header | Bắt buộc | Giá trị |
|---|---|---|
Authorization | Có | bearer <API_KEY> |
Content-Type | Có | application/json (nên kèm charset) |
X-Odoo-Database | Khi cần | Tên database — chỉ bắt buộc khi một domain phục vụ nhiều database |
User-Agent | Nên có | Tên phần mềm của bạn |
Thân request
{
"context": { "lang": "en_US" },
"domain": [
["name", "ilike", "%deco%"],
["is_company", "=", true]
],
"fields": ["name"]
}
Tham số truyền theo tên. Không có chế độ truyền theo vị trí — bạn phải dùng đúng tên tham số được định nghĩa trên phương thức của Odoo (ids, domain, fields, vals, vals_list, v.v.).
Phản hồi
Thành công trả về HTTP 200 kèm giá trị trả về của phương thức dưới dạng JSON.
Lỗi trả về mã 4xx hoặc 5xx kèm một đối tượng lỗi JSON:
{
"name": "werkzeug.exceptions.Unauthorized",
"message": "Invalid apikey",
"arguments": ["Invalid apikey", 401],
"context": {},
"debug": "Traceback (most recent call last): ..."
}
Đây là một thay đổi có ý nghĩa. Với mã trạng thái đúng chuẩn, hệ thống giám sát của bạn phân biệt được một tích hợp đang chạy tốt với một tích hợp đang hỏng mà không phải soi từng phần thân phản hồi.
Hành vi transaction
Mỗi lời gọi tới /json/2 chạy trong một transaction SQL riêng. Thành công thì commit; lỗi thì hủy bỏ. Bạn không thể nối nhiều lời gọi vào cùng một transaction qua API đối ngoại.
Lấy API key
API key gắn với từng người dùng và có thời hạn. Người dùng thường tạo được key với thời hạn tối đa 90 ngày. Quản trị viên tạo được key không hết hạn.
Để tạo key thủ công:
- Vào
Preferences > Account Security - Nhấp New API Key
- Nhập mô tả (hãy dùng thứ gì đó cho biết key này dùng ở đâu — đừng chỉ ghi “API key”)
- Đặt ngày hết hạn — để ngắn nếu dùng theo kiểu tương tác
- Nhấp Generate Key
Key là một giá trị ngẫu nhiên 160 bit. Nó chỉ hiện ra đúng một lần. Hãy chép ngay và cất ngoài mã nguồn — một trình quản lý secret, một biến môi trường, hoặc một vault. Nếu làm mất, bạn phải thu hồi và tạo key mới.
⚠️ Với người dùng thường, key sống tối đa 90 ngày. Nghĩa là các tích hợp cần một quy trình xoay vòng key. Hoặc tự động hóa việc xoay vòng bằng code (xem bên dưới), hoặc đặt lịch nhắc trước mỗi lần key hết hạn.
Tạo key bằng code
Nếu bạn cần xoay vòng key mà không phải thao tác tay trên giao diện:
import requests
API_KEY = ... # current key, from a secure location
res = requests.post(
"https://mycompany.example.com/json/2/res.users.apikeys/generate",
headers={"Authorization": f"bearer {API_KEY}"},
json={
"name": "my-integration-service",
"expiration_date": "2026-09-22",
"scope": None,
},
)
res.raise_for_status()
new_key = res.json() # store this securely, then retire the old key
Một tích hợp chạy được bằng Python
import requests
BASE_URL = "https://mycompany.example.com/json/2"
API_KEY = ... # from a secure location
headers = {
"Authorization": f"bearer {API_KEY}",
"X-Odoo-Database": "mycompany",
"User-Agent": "my-integration " + requests.utils.default_user_agent(),
}
# search
res = requests.post(
f"{BASE_URL}/res.partner/search",
headers=headers,
json={
"context": {"lang": "en_US"},
"domain": [["name", "ilike", "%deco%"], ["is_company", "=", True]],
},
)
res.raise_for_status()
ids = res.json()
# read
res = requests.post(
f"{BASE_URL}/res.partner/read",
headers=headers,
json={
"ids": ids,
"context": {"lang": "en_US"},
"fields": ["name", "email"],
},
)
res.raise_for_status()
records = res.json()
Không phân tích XML. Không có lớp bao RPC. Không phải đi tìm mã trạng thái bị chôn trong một payload JSON. Tầng HTTP mang luôn phần ngữ nghĩa.
Việc chuyển đổi thực tế trông ra sao
Dưới đây là cùng một thao tác — đọc danh sách đối tác — đặt cạnh nhau giữa kiểu XML-RPC cũ và /json/2.
Cũ (XML-RPC, xmlrpc.client của Python):
import xmlrpc.client
url = "https://mycompany.example.com"
db = "mycompany"
username = "admin"
password = "admin_password"
common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, password, {})
models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")
partners = models.execute_kw(
db, uid, password,
"res.partner", "search_read",
[[["name", "ilike", "%deco%"]]],
{"fields": ["name"], "limit": 10}
)
Mới (/json/2, requests):
import requests
partners = requests.post(
"https://mycompany.example.com/json/2/res.partner/search_read",
headers={"Authorization": "bearer <API_KEY>", "X-Odoo-Database": "mycompany"},
json={"domain": [["name", "ilike", "%deco%"]], "fields": ["name"], "limit": 10},
).json()
Bản cũ cần hai vòng đi về (xác thực, rồi mới gọi). Bản mới chỉ cần một. Bản cũ truyền thông tin đăng nhập ở mọi lời gọi. Bản mới gửi một key không bao giờ để lộ mật khẩu của người dùng.
Cần để ý gì khi viết lại
Chỉ truyền tham số theo tên. XML-RPC cho phép truyền theo vị trí. /json/2 thì không. Nếu lời gọi của bạn dùng execute_kw với một danh sách theo vị trí, bạn phải tra chữ ký phương thức và đặt tên tham số một cách tường minh. write nhận vals, không phải một dict theo vị trí. create nhận vals_list khi tạo hàng loạt.
Không có thứ tương đương cho service db hay common. Các service RPC cũ là db và common (liệt kê database, lấy phiên bản máy chủ, xác thực) không nằm trong /json/2. Thông tin phiên bản chuyển sang GET /web/version. Việc lấy ID người dùng làm qua res.users/context_get mà không cần ids — API tự rút người dùng ra từ key.
Chỉ thay /xmlrpc/2/object, không thay /xmlrpc/2/db. Endpoint /json/2 thay cho service object. Việc quản trị database qua RPC không được chuyển sang — và đó là chủ ý.
Các khác biệt chính trong một bảng
| XML-RPC / JSON-RPC | /json/2 | |
|---|---|---|
| Xác thực | Tên đăng nhập + mật khẩu | API key dạng bearer |
| Thời hạn của key | Còn người dùng thì còn hiệu lực | Tối đa 90 ngày (người dùng thường) |
| Cách báo lỗi | Luôn HTTP 200, phải soi phần thân | HTTP 4xx / 5xx |
| Kiểu truyền tham số | Theo vị trí hoặc theo tên | Chỉ theo tên |
| Cấu trúc URL | Cố định /xmlrpc/2/object | /json/2/<model>/<method> |
| Phạm vi transaction | Tùy trường hợp | Một transaction mỗi lời gọi |
| Hỗ trợ số nguyên lớn | Hạn chế (XML-RPC) | Đầy đủ |
| Tài liệu động | Không có | /doc theo từng database |
| Thời điểm gỡ bỏ | Odoo 22 / Online 21.1 | Hiện hành và đang được duy trì |
Lộ trình ngừng hỗ trợ
| Nền tảng | Ngừng hỗ trợ | Gỡ bỏ |
|---|---|---|
| Odoo Online (SaaS) | Odoo 19 | Online 21.1 (đông 2027) |
| Odoo.sh | Odoo 19 | Odoo 22 (thu 2028) |
| On-premise | Odoo 19 | Odoo 22 (thu 2028) |
Nếu bạn đang ở Odoo Online, quỹ thời gian khoảng 18 tháng. Trên Odoo.sh hoặc on-prem, bạn có tới mùa thu 2028. Trong cả hai trường hợp, thời điểm hợp lý để chuyển đổi là trước lần nâng cấp lớn kế tiếp, không phải tuần trước hạn chót.
⚠️ Lưu ý: việc ngừng hỗ trợ áp dụng cho
/xmlrpc,/xmlrpc/2và/jsonrpc. Nó không ảnh hưởng tới các controller nội bộ@route(type='jsonrpc')mà web client của chính Odoo dùng — đó là một hệ thống khác và không bị gỡ bỏ.
Khám phá API ngay trên hệ thống của bạn
Mọi instance Odoo 19 đều phơi ra tài liệu động tại /doc. Trang này cho bạn thấy chính xác các model, phương thức, trường và nhóm quyền có trong database của bạn, kèm ví dụ chạy được. Nếu đang xây một tích hợp mà chưa chắc nên gọi phương thức nào hay nó nhận tham số gì, đó là nơi nên bắt đầu.