WordPress REST API Schema 验证机制详解
各位同学,大家好!今天我们来深入探讨 WordPress REST API 的 Schema 验证机制。Schema 验证是构建健壮、可靠的 API 的关键组成部分,它确保传入和传出的数据符合预定义的结构,从而避免因数据类型错误、缺失字段或无效值导致的错误。
1. 什么是 Schema?
在 REST API 的上下文中,Schema 本质上就是一个描述数据结构的蓝图。它定义了 API 期望接收和返回的数据的形状,包括:
- 数据类型 (Data Type): 例如字符串、整数、布尔值、数组、对象等。
- 字段名称 (Field Name): 每个字段的名称。
- 字段描述 (Field Description): 字段的用途和含义,有助于 API 文档的生成。
- 是否必填 (Required): 指明字段是否必须存在。
- 默认值 (Default Value): 当字段未提供时使用的默认值。
- 验证规则 (Validation Rules): 定义字段值的有效范围和格式。 例如,最小长度、最大长度、正则表达式等。
- 枚举值 (Enum): 允许字段取值的集合。
Schema 的常见格式包括 JSON Schema 和 OpenAPI Schema。WordPress REST API 使用基于 JSON Schema 的格式。
2. WordPress REST API 如何使用 Schema?
WordPress REST API 利用 Schema 来规范 API 的行为,主要体现在以下两个方面:
- 请求验证 (Request Validation): 验证客户端发送的请求数据是否符合预定义的 Schema。如果请求数据不符合 Schema,API 会返回错误。
- 响应序列化 (Response Serialization): 确保 API 返回的数据符合 Schema。这有助于客户端准确地解析和处理响应数据。
3. Schema 的定义和注册
在 WordPress 中,Schema 通常在注册自定义 REST API 路由时定义。可以使用 register_rest_route()
函数来注册路由,并在 args
参数中指定 Schema。
add_action( 'rest_api_init', function () {
register_rest_route( 'my-plugin/v1', '/items', array(
'methods' => 'POST',
'callback' => 'my_plugin_create_item',
'args' => array(
'title' => array(
'required' => true,
'type' => 'string',
'description' => 'The title of the item.',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
'content' => array(
'type' => 'string',
'description' => 'The content of the item.',
'sanitize_callback' => 'wp_kses_post',
'validate_callback' => 'rest_validate_request_arg',
),
'status' => array(
'type' => 'string',
'description' => 'The status of the item.',
'default' => 'draft',
'enum' => array( 'draft', 'pending', 'publish', 'private' ),
'sanitize_callback' => 'sanitize_key',
'validate_callback' => 'rest_validate_request_arg',
),
),
) );
} );
function my_plugin_create_item( WP_REST_Request $request ) {
$title = $request['title'];
$content = $request['content'];
$status = $request['status'];
// 创建文章...
$post_id = wp_insert_post( array(
'post_title' => $title,
'post_content' => $content,
'post_status' => $status,
) );
if ( is_wp_error( $post_id ) ) {
return new WP_Error( 'my_plugin_create_error', 'Failed to create item.', array( 'status' => 500 ) );
}
return rest_ensure_response( array( 'id' => $post_id ) );
}
在这个例子中,args
数组定义了 title
、content
和 status
三个参数的 Schema。
4. Schema 参数详解
下面详细解释 args
数组中常用的参数:
参数名 | 数据类型 | 描述 |
---|---|---|
required |
boolean | 指示该参数是否是必需的。如果为 true ,则在请求中必须包含该参数。 |
type |
string | 指定参数的数据类型。常见的类型包括 string 、integer 、number 、boolean 、array 、object 。 |
description |
string | 描述参数的用途和含义。这对于生成 API 文档非常有用。 |
default |
mixed | 指定参数的默认值。如果请求中未包含该参数,则使用默认值。 |
enum |
array | 定义参数允许的取值集合。参数的值必须是 enum 数组中的一个。 |
sanitize_callback |
callable | 指定用于清理参数值的回调函数。该函数接收参数值作为输入,并返回清理后的值。 |
validate_callback |
callable | 指定用于验证参数值的回调函数。该函数接收参数值作为输入,并返回 true 如果参数值有效,否则返回 false 或 WP_Error 对象。 |
items |
array | 仅在 type 为 array 时使用。定义数组中元素的 Schema。 |
properties |
array | 仅在 type 为 object 时使用。定义对象中属性的 Schema。 |
format |
string | 指定参数的格式。例如,对于 string 类型,可以指定 email 、uri 、date 、date-time 等格式。 |
minimum |
number | 仅在 type 为 integer 或 number 时使用。指定参数的最小值。 |
maximum |
number | 仅在 type 为 integer 或 number 时使用。指定参数的最大值。 |
minLength |
integer | 仅在 type 为 string 时使用。指定参数的最小长度。 |
maxLength |
integer | 仅在 type 为 string 时使用。指定参数的最大长度。 |
pattern |
string | 仅在 type 为 string 时使用。指定参数必须匹配的正则表达式。 |
5. 内置的 Sanitize 和 Validate Callback 函数
WordPress 提供了许多内置的 Sanitize 和 Validate Callback 函数,可以方便地用于 Schema 定义。
-
Sanitize Callback:
sanitize_text_field()
:对字符串进行清理,移除 HTML 标签和特殊字符。wp_kses_post()
:使用 WordPress 的 KSES (Kses HTML filter) 过滤 HTML 内容,只允许安全的标签和属性。sanitize_email()
:清理 Email 地址,确保其格式正确。absint()
:将值转换为绝对整数。sanitize_key()
: 清理用作数据库键或 URL 中的键的字符串。只允许小写字母、数字和连字符。
-
Validate Callback:
rest_validate_request_arg()
:这是一个通用的验证函数,它会根据参数的type
和其他约束条件(如enum
、minimum
、maximum
等)来验证参数值。强烈建议使用这个函数作为默认的验证回调,因为它已经处理了大多数常见的验证场景。
6. 自定义 Sanitize 和 Validate Callback 函数
除了使用内置的函数外,还可以创建自定义的 Sanitize 和 Validate Callback 函数,以满足特定的需求。
-
自定义 Sanitize Callback:
function my_plugin_sanitize_url( $url ) { // 确保 URL 以 http:// 或 https:// 开头 if ( strpos( $url, 'http://' ) !== 0 && strpos( $url, 'https://' ) !== 0 ) { $url = 'http://' . $url; } return esc_url_raw( $url ); // 使用 WordPress 的 esc_url_raw() 函数进行转义 }
-
自定义 Validate Callback:
function my_plugin_validate_date( $date, $request, $param ) { // 使用 strtotime() 函数检查日期是否有效 if ( strtotime( $date ) === false ) { return new WP_Error( 'my_plugin_invalid_date', 'The date is invalid.', array( 'status' => 400 ) ); } return true; }
注意: Validate Callback 函数必须返回
true
如果参数有效,否则返回false
或WP_Error
对象。如果返回WP_Error
对象,API 会返回一个错误响应。
7. 处理数组和对象类型的 Schema
当参数的 type
为 array
或 object
时,需要使用 items
或 properties
参数来定义数组元素或对象属性的 Schema。
-
数组类型的 Schema:
'tags' => array( 'type' => 'array', 'description' => 'An array of tags.', 'items' => array( 'type' => 'string', // 数组中的元素是字符串 ), 'sanitize_callback' => 'my_plugin_sanitize_array', 'validate_callback' => 'rest_validate_request_arg', ), function my_plugin_sanitize_array( $array ) { if ( ! is_array( $array ) ) { return array(); } return array_map( 'sanitize_text_field', $array ); }
-
对象类型的 Schema:
'author' => array( 'type' => 'object', 'description' => 'The author of the item.', 'properties' => array( 'name' => array( 'type' => 'string', 'description' => 'The name of the author.', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ), 'email' => array( 'type' => 'string', 'format' => 'email', 'description' => 'The email of the author.', 'sanitize_callback' => 'sanitize_email', 'validate_callback' => 'rest_validate_request_arg', ), ), ),
8. 使用 WP_REST_Request
对象
在 API 回调函数中,可以使用 WP_REST_Request
对象来访问请求参数。WP_REST_Request
对象提供了一些方法来获取参数值,例如:
$request->get_param( $param_name )
:获取指定参数的值。$request->get_query_params()
:获取 URL 查询参数。$request->get_body_params()
:获取请求体中的参数(通常用于 POST 和 PUT 请求)。$request->get_json_params()
:获取 JSON 格式的请求体参数。$request[ $param_name ]
: 直接使用数组访问方式。
9. 错误处理
当请求数据不符合 Schema 时,WordPress REST API 会自动返回一个错误响应。可以使用 WP_Error
对象来创建自定义的错误响应。
function my_plugin_create_item( WP_REST_Request $request ) {
$title = $request['title'];
if ( empty( $title ) ) {
return new WP_Error( 'my_plugin_missing_title', 'The title is required.', array( 'status' => 400 ) );
}
// ...
}
WP_Error
对象包含以下属性:
$code
: 错误代码(字符串)。$message
: 错误消息(字符串)。$data
: 附加数据(数组)。 通常包含status
键,用于指定 HTTP 状态码。
10. 示例:更复杂的 Schema 结构
假设我们需要创建一个 API 来管理书籍信息,书籍信息包括:
title
(string, 必填): 书籍标题author
(object, 必填): 作者信息,包含:name
(string, 必填): 作者姓名email
(string, 必填, email格式): 作者邮箱
publication_date
(string, 可选, date格式): 出版日期tags
(array, 可选): 标签列表,每个标签都是一个字符串price
(number, 必填, 最小值0): 价格
add_action( 'rest_api_init', function () {
register_rest_route( 'my-plugin/v1', '/books', array(
'methods' => 'POST',
'callback' => 'my_plugin_create_book',
'args' => array(
'title' => array(
'required' => true,
'type' => 'string',
'description' => 'The title of the book.',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
'author' => array(
'required' => true,
'type' => 'object',
'description' => 'The author of the book.',
'properties' => array(
'name' => array(
'required' => true,
'type' => 'string',
'description' => 'The name of the author.',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
'email' => array(
'required' => true,
'type' => 'string',
'format' => 'email',
'description' => 'The email of the author.',
'sanitize_callback' => 'sanitize_email',
'validate_callback' => 'rest_validate_request_arg',
),
),
),
'publication_date' => array(
'type' => 'string',
'format' => 'date',
'description' => 'The publication date of the book.',
'sanitize_callback' => 'sanitize_text_field', // 可以使用更合适的日期清理函数
'validate_callback' => 'my_plugin_validate_date',
),
'tags' => array(
'type' => 'array',
'description' => 'An array of tags.',
'items' => array(
'type' => 'string',
),
'sanitize_callback' => 'my_plugin_sanitize_array',
'validate_callback' => 'rest_validate_request_arg',
),
'price' => array(
'required' => true,
'type' => 'number',
'description' => 'The price of the book.',
'minimum' => 0,
'sanitize_callback' => 'floatval', // 确保是浮点数
'validate_callback' => 'rest_validate_request_arg',
),
),
) );
} );
function my_plugin_create_book( WP_REST_Request $request ) {
$title = $request['title'];
$author = $request['author'];
$publication_date = $request['publication_date'];
$tags = $request['tags'];
$price = $request['price'];
// 简单验证 author 是否是数组
if ( ! is_array( $author ) || ! isset( $author['name'] ) || ! isset( $author['email'] ) ) {
return new WP_Error( 'my_plugin_invalid_author', 'The author must be an object with name and email properties.', array( 'status' => 400 ) );
}
// 创建书籍的相关逻辑
$book_data = array(
'title' => $title,
'author_name' => $author['name'],
'author_email' => $author['email'],
'publication_date' => $publication_date,
'tags' => $tags,
'price' => $price,
);
// 假设存储到自定义表中
global $wpdb;
$table_name = $wpdb->prefix . 'my_books';
$result = $wpdb->insert(
$table_name,
$book_data,
array(
'%s', '%s', '%s', '%s', '%s', '%f' // 数据类型
)
);
if ( false === $result ) {
return new WP_Error( 'my_plugin_create_book_error', 'Failed to create book.', array( 'status' => 500 ) );
}
$book_id = $wpdb->insert_id;
return rest_ensure_response( array( 'id' => $book_id ) );
}
function my_plugin_validate_date( $date, $request, $param ) {
if ( empty( $date ) ) {
return true; // 允许为空,因为 publication_date 不是必需的
}
$d = DateTime::createFromFormat('Y-m-d', $date);
if ( !($d && $d->format('Y-m-d') == $date) ) {
return new WP_Error( 'my_plugin_invalid_date', 'The date format is invalid. Use YYYY-MM-DD.', array( 'status' => 400 ) );
}
return true;
}
function my_plugin_sanitize_array( $array ) {
if ( ! is_array( $array ) ) {
return array();
}
return array_map( 'sanitize_text_field', $array );
}
11. 总结
Schema 验证是构建健壮 API 的基石,它通过预定义的结构来规范 API 的数据交互。通过 register_rest_route
函数和 args
参数,我们可以定义 API 的 Schema,并使用内置或自定义的 Sanitize 和 Validate Callback 函数来确保数据的有效性和安全性。 良好的错误处理机制同样重要,能够帮助客户端更好地理解 API 的行为并处理潜在的问题。 遵循这些原则,能够构建出高质量、易于维护的 WordPress REST API。