关于 frappe-syntax-whitelisted
面向 Frappe 开发者的参考技能,介绍如何将 Python 方法设为 HTTP API 端点,涵盖端点配置、输入校验、权限与客户端调用。
主要功能
- 01配置白名单装饰器选项
- 02检查权限并校验输入
- 03处理错误与响应格式
- 04通过 JavaScript 或 REST 调用 API
- 05为访客端点设置速率限制
适用场景
- 01将 Python 函数公开为 API 端点
- 02保护访问文档的方法
- 03使用令牌认证连接外部客户端
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-syntax-whitelisted安装后在 Claude Code 中输入/frappe-syntax-whitelisted确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/anti-patterns.mdclient-calls.mddecorator-options.mderror-handling.mdexamples.mdhooks.mdmethods.mdparameter-handling.mdpatterns.mdpermission-patterns.mdresponse-patterns.mdsyntax.md详细介绍
Frappe 语法:白名单方法
白名单方法通过 /api/method/ 将 Python 函数公开为 HTTP API 端点。
快速参考
import frappe
from frappe import _
# Authenticated endpoint (default)
@frappe.whitelist()
def get_customer_summary(customer):
frappe.has_permission("Customer", "read", throw=True)
return frappe.get_doc("Customer", customer).as_dict()
# Public endpoint — ALWAYS validate input thoroughly
@frappe.whitelist(allow_guest=True, methods=["POST"])
def submit_contact(name, email, message):
if not name or not email:
frappe.throw(_("Name and email required"), frappe.ValidationError)
return {"success": True}
# Controller method — called via frm.call('method_name')
class SalesOrder(Document):
@frappe.whitelist()
def calculate_taxes(self, include_shipping=False):
return {"tax": self.grand_total * 0.21}
端点 URL:/api/method/myapp.module.function_name
装饰器签名 [v14+]
@frappe.whitelist(
allow_guest=False, # True = accessible without login
xss_safe=False, # True = do NOT escape HTML in response
methods=None, # ["GET"], ["POST"], or ["GET","POST"] — default: all
force_types=None # True = require type annotations [v15+]
)
| 参数 | 默认值 | 效果 |
|---|---|---|
allow_guest | False | True = Guest 角色可以调用;务必添加额外的输入验证 |
xss_safe | False | True = 不转义 HTML;切勿在未清理输出的情况下使用 |
methods | None(全部) | 限制允许的 HTTP 方法 |
force_types | None | True = 所有参数都必须有类型注解 [v15+] |
完整详情:decorator-options.md
决策树
What kind of endpoint?
|
+-- Standalone API (utility, integration, dashboard)?
| --> @frappe.whitelist() on a module-level function
| --> Call via: frappe.call('myapp.api.function')
| --> URL: /api/method/myapp.api.function
|
+-- Document-specific action?
| --> @frappe.whitelist() on a Document class method
| --> Call via: frm.call('method_name')
| --> URL: /api/method/run_doc_method (internal)
|
+-- Server Script (no-code)?
--> Use Server Script DocType instead (no decorator needed)
Who may call the API?
|
+-- Anyone (including guests)?
| --> allow_guest=True + thorough input validation + rate limiting
|
+-- Logged-in users only?
+-- Specific role? --> frappe.only_for("RoleName")
+-- DocType-level? --> frappe.has_permission(doctype, ptype, throw=True)
+-- Document-level? --> frappe.has_permission(doctype, ptype, doc, throw=True)
Which HTTP methods?
|
+-- Read only? --> methods=["GET"]
+-- Write only? --> methods=["POST"]
+-- Both? --> methods=["GET","POST"] or default
权限模式
务必在每个白名单方法中检查权限。@frappe.whitelist() 装饰器只会验证用户是否已登录,不会检查 DocType 或文档级别的权限。
# DocType-level permission (throw=True raises PermissionError automatically)
@frappe.whitelist()
def get_orders():
frappe.has_permission("Sales Order", "read", throw=True)
return frappe.get_all("Sales Order", limit=20)
# Document-level permission
@frappe.whitelist()
def get_order(name):
frappe.has_permission("Sales Order", "read", name, throw=True)
return frappe.get_doc("Sales Order", name).as_dict()
# Role-based restriction
@frappe.whitelist()
def admin_action():
frappe.only_for("System Manager") # throws if user lacks role
return {"secret": "data"}
参数处理
HTTP 请求中的参数以字符串形式传入。务必显式转换。
@frappe.whitelist()
def calculate(amount, quantity, items=None):
amount = float(amount) # ALWAYS cast numeric params
quantity = int(quantity)
if isinstance(items, str): # ALWAYS parse JSON strings
items = frappe.parse_json(items)
return amount * quantity
通过 frappe.form_dict 访问所有请求参数:
@frappe.whitelist()
def dynamic_handler():
all_params = frappe.form_dict
customer = frappe.form_dict.get("customer")
类型注解 [v15+]
Frappe v15+ 会在请求时通过 Pydantic 自动验证类型注解:
@frappe.whitelist()
def get_orders(customer: str, limit: int = 10, active: bool = True) -> dict:
# Frappe auto-validates: limit MUST be convertible to int
return {"orders": frappe.get_all("Sales Order", limit=limit)}
force_types 和 require_type_annotated_api_methods [v15+]
@frappe.whitelist(force_types=True)— 每个参数都必须有类型注解- 通过
hooks.py在应用级别强制执行:require_type_annotated_api_methods = 1 - 缺少注解时会引发
FrappeTypeError
客户端调用
frappe.call():独立 API
// Promise-based (ALWAYS prefer this)
frappe.call({
method: 'myapp.api.get_summary',
args: { customer: 'CUST-001' },
freeze: true,
freeze_message: __('Loading...')
}).then(r => {
console.log(r.message); // return value is in r.message
}).catch(err => {
frappe.show_alert({ message: __('Error'), indicator: 'red' });
});
frm.call():控制器方法
frm.call('calculate_taxes', { include_shipping: true })
.then(r => frm.set_value('tax_amount', r.message.tax_amount));
REST API(外部客户端)
# Token auth (ALWAYS use for external integrations)
curl -H "Authorization: token api_key:api_secret" \
-H "Content-Type: application/json" \
-X POST https://site.com/api/method/myapp.api.create_order \
-d '{"customer": "CUST-001"}'
完整模式:client-calls.md
错误处理
@frappe.whitelist()
def process_order(order_id):
if not order_id:
frappe.throw(_("Order ID required"), frappe.ValidationError)
if not frappe.has_permission("Sales Order", "write", order_id):
frappe.throw(_("Not permitted"), frappe.PermissionError)
try:
result = heavy_operation(order_id)
return {"success": True, "data": result}
except Exception:
frappe.log_error(frappe.get_traceback(), "process_order")
frappe.throw(_("Operation failed. Contact support."))
| 异常 | HTTP 代码 | 使用场景 |
|---|---|---|
frappe.ValidationError | 417 | 输入验证失败 |
frappe.PermissionError | 403 | 访问被拒绝 |
frappe.DoesNotExistError | 404 | 找不到文档 |
frappe.DuplicateEntryError | 409 | 记录重复 |
frappe.AuthenticationError | 401 | 未登录 |
完整模式:error-handling.md
响应模式
# Return value auto-wraps as {"message": <return_value>}
@frappe.whitelist()
def get_data():
return {"key": "value"} # Client receives: {"message": {"key": "value"}}
# Custom HTTP status
@frappe.whitelist()
def create_item(data):
doc = frappe.get_doc(data).insert()
frappe.local.response["http_status_code"] = 201
return {"name": doc.name}
# File download
@frappe.whitelist()
def download_report(name):
content = generate_pdf(name)
frappe.response.filename = f"{name}.pdf"
frappe.response.filecontent = content
frappe.response.type = "download"
完整模式:response-patterns.md
速率限制 [v14+]
from frappe.rate_limiter import rate_limit
@frappe.whitelist(allow_guest=True)
@rate_limit(limit=5, seconds=60) # 5 requests per 60 seconds per IP
def public_endpoint():
return {"status": "ok"}
rate_limit 签名:
rate_limit(key=None, limit=5, seconds=86400, methods="ALL", ip_based=True)
务必在 allow_guest=True 的端点上使用 @rate_limit,以防止滥用。
版本差异
| 功能 | v14 | v15+ | v16+ |
|---|---|---|---|
@frappe.whitelist() | 是 | 是 | 是 |
allow_guest、xss_safe、methods | 是 | 是 | 是 |
| 类型注解验证 | 否 | 是(通过 Pydantic 自动验证) | 是 |
force_types 参数 | 否 | 是 | 是 |
require_type_annotated_api_methods 钩子 | 否 | 是 | 是 |
@rate_limit() 装饰器 | 是 | 是 | 是 |
缺少注解时触发 FrappeTypeError | 否 | 是 | 是 |
关键规则
- 切勿跳过权限检查 —
@frappe.whitelist()只确认用户已登录,不验证授权 - 切勿在原始 SQL 中使用用户输入 — 务必使用参数化查询或 ORM
- 切勿泄露堆栈跟踪 — 使用
frappe.log_error()记录日志,并显示通用消息 - 务必验证输入类型 — HTTP 请求中的参数以字符串形式传入
- 务必在 Guest 端点上使用
@rate_limit— 防止滥用 - 切勿在未先检查角色的情况下使用
ignore_permissions=True - 务必对复杂的 JS 参数使用
JSON.stringify()— 包括数组和对象
完整反模式:anti-patterns.md
安全检查清单
对于每个列入白名单的方法,请确认:
- 存在权限检查(
frappe.has_permission()或frappe.only_for()) - 已验证输入(类型、范围、格式)
- SQL 查询使用参数化(绝不进行字符串插值)
- 错误消息不包含内部细节
- 仅在有明确理由且启用速率限制时使用
allow_guest=True - 仅在前置角色检查后使用
ignore_permissions=True - 尽可能限制 HTTP 方法
- 响应仅包含必要字段(不泄露敏感数据)
参考文件
| 文件 | 内容 |
|---|---|
| decorator-options.md | 所有 @frappe.whitelist() 参数和 force_types |
| parameter-handling.md | 请求参数、类型转换、frappe.form_dict |
| response-patterns.md | 返回类型、文件下载、流式传输、HTTP 状态 |
| client-calls.md | frappe.call()、frm.call()、REST API、fetch 模式 |
| permission-patterns.md | 权限检查、角色防护、自定义逻辑 |
| error-handling.md | 异常类型、frappe.throw()、日志记录 |
| examples.md | 完整可运行的 API 示例 |
| anti-patterns.md | 安全错误和性能陷阱 |
| hooks.md | 在 hooks.py 中声明列入白名单的方法 |
| syntax.md | 装饰器核心语法和注册机制 |
评论
支持 Markdown