WordPress源码深度解析之:`WordPress`的`REST API`:`REST`端点的`Schema`验证。

各位靓仔靓女,今天咱们来聊聊WordPress REST API里的“Schema验证”,这玩意儿听起来有点高大上,但其实就像咱们去餐厅点菜,菜单(Schema)告诉你有什么菜(数据),你点了什么(请求),服务员(验证)会看看你点的菜是不是真的在菜单上,免得你点了“飞天意面神教的意大利面”这种不存在的东西。

一、 啥是Schema,为啥要验证?

Schema,中文可以理解为“模式”或者“结构”,在REST API里,它描述了API端点(endpoint)接收的数据格式和返回的数据格式。 简单来说,就是告诉调用者:

  • 请求(Request): 你需要传哪些参数,每个参数是什么类型(字符串、数字、数组等),是不是必须的。
  • 响应(Response): API会返回什么样的数据,每个字段是什么类型,有没有默认值。

为啥要验证呢? 想象一下,如果没有验证,你辛辛苦苦写了个APP,结果API给你返回了个乱七八糟的数据,你的APP直接崩了。验证就像一道防火墙,确保数据的质量,防止垃圾数据污染你的系统。

二、 WordPress REST API Schema的结构

WordPress的REST API Schema是基于JSON Schema标准构建的。JSON Schema定义了一种描述JSON数据结构的语言。它允许你定义字段类型、约束条件(比如最小值、最大长度)、枚举值等等。

一个典型的WordPress REST API Schema就像下面这样(简化版,以文章(post)为例):

{
  "title": "Post",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique identifier for the object.",
      "readonly": true
    },
    "date": {
      "type": "string",
      "format": "date-time",
      "description": "The date the object was published, in the site's timezone."
    },
    "title": {
      "type": "object",
      "description": "The title for the object.",
      "properties": {
        "raw": {
          "type": "string",
          "description": "Title for the object, as it exists in the database."
        },
        "rendered": {
          "type": "string",
          "description": "HTML title for the object, transformed for display."
        }
      }
    },
    "content": {
      "type": "object",
      "description": "The content for the object.",
      "properties": {
        "raw": {
          "type": "string",
          "description": "Content for the object, as it exists in the database."
        },
        "rendered": {
          "type": "string",
          "description": "HTML content for the object, transformed for display."
        }
      }
    },
    "status": {
      "type": "string",
      "description": "A named status for the object.",
      "enum": [
        "publish",
        "future",
        "draft",
        "pending",
        "private"
      ]
    }
  }
}

咱们来解读一下:

  • title: 这个Schema的名字,告诉你这是描述什么的。
  • type: 这个Schema描述的是什么类型的数据,这里是object,也就是一个JSON对象。
  • properties: 这个对象有哪些属性,每个属性又是什么类型。比如id是整数(integer),date是字符串,并且格式是日期时间(date-time)。
  • readonly: 表示这个属性是只读的,不能通过API修改。
  • enum: 表示这个属性只能是枚举值里的某一个,比如status只能是publish, future, draft, pending, private中的一个。

三、 如何获取WordPress REST API的Schema?

WordPress REST API提供了一个特殊的端点来获取Schema,格式是:

/wp-json/wp/v2/<resource>/schema

其中<resource>是你想要获取Schema的资源,比如postscategoriesusers等等。

例如,获取文章(posts)的Schema:

GET /wp-json/wp/v2/posts/schema

你可以使用任何HTTP客户端(比如curlPostman)来发送请求。

四、 如何利用Schema进行验证?

WordPress在内部使用Schema来验证请求数据,确保数据的有效性。 你也可以在你的插件或者主题中使用Schema来进行验证。

1. 使用WP_REST_Request进行验证

WordPress提供了一个WP_REST_Request类,可以用来创建REST API请求对象,并且可以根据Schema进行验证。

<?php

use WP_REST_Request;
use WP_Error;

// 创建一个REST API请求对象
$request = new WP_REST_Request( 'POST', '/wp/v2/posts' );

// 设置请求参数
$request->set_param( 'title', 'My New Post' );
$request->set_param( 'content', 'This is the content of my new post.' );
$request->set_param( 'status', 'publish' );

// 获取文章的Schema
$schema = rest_get_endpoint( '/wp/v2/posts' )->get_item_schema_for_update();

// 验证请求参数
$params = $request->get_params();
$errors = new WP_Error();

foreach ( $schema['properties'] as $property => $details ) {
    if ( isset( $params[ $property ] ) ) {
        $value = $params[ $property ];

        // 类型验证
        if ( isset( $details['type'] ) ) {
            switch ( $details['type'] ) {
                case 'integer':
                    if ( ! is_numeric( $value ) || intval( $value ) != $value ) {
                        $errors->add(
                            'invalid_parameter',
                            sprintf( 'The "%s" parameter must be an integer.', $property ),
                            array( 'status' => 400 )
                        );
                    }
                    break;
                case 'string':
                    if ( ! is_string( $value ) ) {
                        $errors->add(
                            'invalid_parameter',
                            sprintf( 'The "%s" parameter must be a string.', $property ),
                            array( 'status' => 400 )
                        );
                    }
                    break;
                // 可以添加更多类型验证
            }
        }

        // 枚举值验证
        if ( isset( $details['enum'] ) && ! in_array( $value, $details['enum'], true ) ) {
            $errors->add(
                'invalid_parameter',
                sprintf( 'The "%s" parameter must be one of: %s', $property, implode( ', ', $details['enum'] ) ),
                array( 'status' => 400 )
            );
        }

        // 其他验证规则可以根据Schema的定义添加
    }
}

// 如果有错误,返回错误信息
if ( ! empty( $errors->get_error_messages() ) ) {
    return $errors;
}

// 如果验证通过,继续处理请求
// ...
?>

这段代码做了这些事情:

  1. 创建了一个WP_REST_Request对象,模拟一个POST请求,请求的端点是/wp/v2/posts,也就是创建文章的端点。
  2. 设置了请求的参数,包括titlecontentstatus
  3. 获取了文章的Schema,使用rest_get_endpoint函数,传入端点URL,然后调用get_item_schema_for_update方法。
  4. 遍历Schema的properties,对每个参数进行验证。
  5. 验证类型,比如integerstring等。
  6. 验证枚举值,比如status只能是publishfuture等。
  7. 如果有错误,就添加到WP_Error对象中。
  8. 最后,如果WP_Error对象不为空,就返回错误信息。

2. 使用第三方库进行验证

除了手动编写验证代码,你还可以使用第三方库来简化验证过程。 比如OpisJsonSchema库,它是一个强大的JSON Schema验证器。

首先,你需要安装这个库:

composer require opis/json-schema

然后,你可以这样使用:

<?php

require 'vendor/autoload.php';

use OpisJsonSchema{Validator, ValidationResult, ValidationError};

// 你的JSON数据
$data = [
  'title' => 'My New Post',
  'content' => 'This is the content of my new post.',
  'status' => 'publish'
];

// 获取文章的Schema
$schema = rest_get_endpoint( '/wp/v2/posts' )->get_item_schema_for_update();

// 创建一个验证器
$validator = new Validator();

// 验证数据
$result = $validator->validate( $data, $schema );

if ( $result->isValid() ) {
    echo "Data is valid!";
} else {
    echo "Data is invalid:n";
    /** @var ValidationError $error */
    foreach ( $result->getErrors() as $error ) {
        echo sprintf(
            "  * %s at %sn",
            $error->keyword(),
            implode( '/', $error->dataPointer() )
        );
    }
}
?>

这段代码做了这些事情:

  1. 引入了OpisJsonSchema库。
  2. 定义了你的JSON数据。
  3. 获取了文章的Schema。
  4. 创建了一个Validator对象。
  5. 调用validate方法,传入数据和Schema。
  6. 如果验证通过,就输出"Data is valid!"。
  7. 如果验证失败,就输出错误信息,包括错误关键字和数据指针。

五、 自定义REST API端点的Schema

如果你想创建自己的REST API端点,你需要定义自己的Schema。

WordPress提供了一些函数来帮助你定义Schema:

  • register_rest_field(): 注册一个自定义字段,并定义它的Schema。
  • get_item_schema(): 返回端点的Schema。
  • get_item_schema_for_create(): 返回创建项目的Schema。
  • get_item_schema_for_update(): 返回更新项目的Schema。

下面是一个例子,演示如何注册一个自定义字段,并定义它的Schema:

<?php

add_action( 'rest_api_init', 'register_my_custom_field' );

function register_my_custom_field() {
  register_rest_field(
    'post', // 资源类型,这里是文章(post)
    'my_custom_field', // 字段名
    array(
      'get_callback'    => 'get_my_custom_field', // 获取字段值的回调函数
      'update_callback' => 'update_my_custom_field', // 更新字段值的回调函数
      'schema'          => array( // Schema定义
        'description' => 'My custom field.',
        'type'        => 'string',
        'context'     => array( 'view', 'edit' ), // 允许访问的上下文
      ),
    )
  );
}

function get_my_custom_field( $object, $field_name, $request ) {
  // 从文章的meta数据中获取字段值
  return get_post_meta( $object['id'], $field_name, true );
}

function update_my_custom_field( $value, $object, $field_name ) {
  // 更新文章的meta数据
  return update_post_meta( $object->ID, $field_name, $value );
}
?>

这段代码做了这些事情:

  1. 使用register_rest_field函数注册了一个自定义字段,字段名是my_custom_field
  2. 定义了get_callbackupdate_callback函数,分别用于获取和更新字段值。
  3. 定义了schema数组,描述了字段的类型、描述和允许访问的上下文。

六、 Schema验证的常见问题及解决方案

  • Schema定义不完整: Schema应该尽可能详细地描述数据的结构,包括字段类型、约束条件、枚举值等等。
  • 数据类型不匹配: 确保传递的数据类型与Schema定义的类型一致。
  • 缺少必需的参数: 如果Schema定义了某个参数是必需的,必须在请求中包含该参数。
  • 枚举值错误: 如果Schema定义了某个参数的枚举值,必须传递枚举值中的一个。
  • 权限问题: 确保用户有权限访问和修改相应的字段。

七、总结

Schema验证是WordPress REST API的重要组成部分,它确保数据的质量,防止垃圾数据污染你的系统。 通过理解Schema的结构,学习如何获取和使用Schema,以及如何自定义Schema,你可以更好地利用WordPress REST API来构建强大的应用程序。

希望今天的讲解对你有所帮助。 记住,Schema验证就像餐厅的服务员,保证你点的菜都是菜单上有的,不会让你吃到“飞天意面神教的意大利面”。 祝各位编码愉快,bug远离!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注