概要
スキーマは、データがどのように構造化されているかを示すメタデータです。ほとんどのデータベースは、データをより構造的に考えることを可能にするスキーマの何らかの形式を実装しています。WordPress REST APIは、データの構造化を処理するためにJSON Schemaを利用しています。スキーマを使用せずにエンドポイントを実装することもできますが、多くのことを見逃すことになります。何が最適かはあなた次第です。
JSON
まず、JSONについて少し話しましょう。JSONは、人間が読みやすいデータ形式で、JavaScriptオブジェクトに似ています。JSONはJavaScript Object Notationの略です。JSONは急速に人気が高まっており、データ構造の世界を席巻しているようです。WordPress REST APIは、JSONスキーマとして知られる特別な仕様を使用しています。JSON Schemaについて詳しく知りたい場合は、JSON Schemaウェブサイトや、このより理解しやすいJSON Schemaの紹介をチェックしてください。スキーマは多くの利点を提供します:テストの改善、発見性、全体的な構造の向上です。JSONデータの塊を見てみましょう。
{
"shouldBeArray": 'LOL definitely not an array',
"shouldBeInteger": ['lolz', 'you', 'need', 'schema'],
"shouldBeString": 123456789
}
JSONパーサーは、そのデータを問題なく処理し、何も文句を言いません。なぜなら、それは有効なJSONだからです。クライアントとサーバーはデータについて何も知らず、何を期待するかもわかりません。ただJSONを見るだけです。スキーマを実装することで、実際にコードベースを簡素化できます。スキーマはデータをより良く構造化するのに役立ち、アプリケーションがWordPress REST APIとの相互作用をより簡単に考えることができるようになります。WordPress REST APIはスキーマの使用を強制しませんが、推奨されています。スキーマデータはAPIに組み込まれる方法が2つあります;リソースのスキーマと登録された引数のスキーマです。
リソーススキーマ
リソースのスキーマは、特定のオブジェクトにどのフィールドが存在するかを示します。ルートを登録する際に、ルートのリソーススキーマも指定できます。JSONスキーマのPHP表現におけるシンプルなコメントスキーマがどのように見えるかを見てみましょう。
// Register our routes.
function prefix_register_my_comment_route() {
register_rest_route( 'my-namespace/v1', '/comments', array(
// Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
array(
'methods' => 'GET',
'callback' => 'prefix_get_comment_sample',
),
// Register our schema callback.
'schema' => 'prefix_get_comment_schema',
) );
}
add_action( 'rest_api_init', 'prefix_register_my_comment_route' );
/**
* Grabs the five most recent comments and outputs them as a rest response.
*
* @param WP_REST_Request $request Current request.
*/
function prefix_get_comment_sample( $request ) {
$args = array(
'number' => 5,
);
$comments = get_comments( $args );
$data = array();
if ( empty( $comments ) ) {
return rest_ensure_response( $data );
}
foreach ( $comments as $comment ) {
$response = prefix_rest_prepare_comment( $comment, $request );
$data[] = prefix_prepare_for_collection( $response );
}
// Return all of our comment response data.
return rest_ensure_response( $data );
}
/**
* Matches the comment data to the schema we want.
*
* @param WP_Comment $comment The comment object whose response is being prepared.
*/
function prefix_rest_prepare_comment( $comment, $request ) {
$comment_data = array();
$schema = prefix_get_comment_schema();
// We are also renaming the fields to more understandable names.
if ( isset( $schema['properties']['id'] ) ) {
$comment_data['id'] = (int) $comment->comment_ID;
}
if ( isset( $schema['properties']['author'] ) ) {
$comment_data['author'] = (int) $comment->user_id;
}
if ( isset( $schema['properties']['content'] ) ) {
$comment_data['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
}
return rest_ensure_response( $comment_data );
}
/**
* Prepare a response for inserting into a collection of responses.
*
* This is copied from WP_REST_Controller class in the WP REST API v2 plugin.
*
* @param WP_REST_Response $response Response object.
* @return array Response data, ready for insertion into collection data.
*/
function prefix_prepare_for_collection( $response ) {
if ( ! ( $response instanceof WP_REST_Response ) ) {
return $response;
}
$data = (array) $response->get_data();
$links = rest_get_server()::get_compact_response_links( $response );
if ( ! empty( $links ) ) {
$data['_links'] = $links;
}
return $data;
}
/**
* Get our sample schema for comments.
*/
function prefix_get_comment_schema() {
$schema = array(
// This tells the spec of JSON Schema we are using which is draft 4.
'$schema' => 'http://json-schema.org/draft-04/schema#',
// The title property marks the identity of the resource.
'title' => 'comment',
'type' => 'object',
// In JSON Schema you can specify object properties in the properties attribute.
'properties' => array(
'id' => array(
'description' => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
'type' => 'integer',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
'author' => array(
'description' => esc_html__( 'The id of the user object, if author was a user.', 'my-textdomain' ),
'type' => 'integer',
),
'content' => array(
'description' => esc_html__( 'The content for the object.', 'my-textdomain' ),
'type' => 'string',
),
),
);
return $schema;
}
各コメントリソースが、指定したスキーマに一致していることに気づくでしょう。この変更はprefix_rest_prepare_comment()
で行いました。リソースのスキーマを作成することで、OPTIONS
リクエストを行うことでこのスキーマを表示できるようになりました。これはなぜ便利なのでしょうか?他の言語、例えばJavaScriptが私たちのデータを解釈し、エンドポイントからのデータを検証するためには、JavaScriptが私たちのデータがどのように構造化されているかを知る必要があります。スキーマを提供することで、他の著者や私たち自身が、一貫した方法でエンドポイントの上に構築するための扉を開きます。
スキーマは機械可読データを提供するため、JSONを読み取ることができるものは何でも、どのようなデータを見ているのかを理解できます。APIインデックスを見て、GET
リクエストをhttps://ourawesomesite.com/wp-json/`, we are returned the schema of our API, enabling others to write client libraries to interpret our data. This process of reading schema data is known as discovery. When we have provided schema for a resource we make that resource discoverable via
OPTIONS`リクエストに行うと、リソーススキーマを公開することは、スキーマパズルの一部に過ぎません。登録された引数のためにもスキーマを使用したいと考えています。
引数スキーマ
エンドポイントのリクエスト引数を登録する際に、JSONスキーマを使用して引数がどのようなものであるべきかのデータを提供することもできます。これにより、エンドポイントが拡張されるにつれて再利用可能な検証ライブラリを書くことが可能になります。スキーマは最初は手間がかかりますが、成長するプロダクションアプリケーションを書くつもりなら、スキーマの使用を検討すべきです。引数スキーマと検証の使用例を見てみましょう。
// Register our routes.
function prefix_register_my_arg_route() {
register_rest_route( 'my-namespace/v1', '/schema-arg', array(
// Here we register our endpoint.
array(
'methods' => 'GET',
'callback' => 'prefix_get_item',
'args' => prefix_get_endpoint_args(),
),
) );
}
// Hook registration into 'rest_api_init' hook.
add_action( 'rest_api_init', 'prefix_register_my_arg_route' );
/**
* Returns the request argument `my-arg` as a rest response.
*
* @param WP_REST_Request $request Current request.
*/
function prefix_get_item( $request ) {
// If we didn't use required in the schema this would throw an error when my arg is not set.
return rest_ensure_response( $request['my-arg'] );
}
/**
* Get the argument schema for this example endpoint.
*/
function prefix_get_endpoint_args() {
$args = array();
// Here we add our PHP representation of JSON Schema.
$args['my-arg'] = array(
'description' => esc_html__( 'This is the argument our endpoint returns.', 'my-textdomain' ),
'type' => 'string',
'validate_callback' => 'prefix_validate_my_arg',
'sanitize_callback' => 'prefix_sanitize_my_arg',
'required' => true,
);
return $args;
}
/**
* Our validation callback for `my-arg` parameter.
*
* @param mixed $value Value of the my-arg parameter.
* @param WP_REST_Request $request Current request object.
* @param string $param The name of the parameter in this case, 'my-arg'.
* @return true|WP_Error True if the data is valid, WP_Error otherwise.
*/
function prefix_validate_my_arg( $value, $request, $param ) {
$attributes = $request->get_attributes();
if ( isset( $attributes['args'][ $param ] ) ) {
$argument = $attributes['args'][ $param ];
// Check to make sure our argument is a string.
if ( 'string' === $argument['type'] && ! is_string( $value ) ) {
return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%1$s is not of type %2$s', 'my-textdomain' ), $param, 'string' ), array( 'status' => 400 ) );
}
} else {
// This code won't execute because we have specified this argument as required.
// If we reused this validation callback and did not have required args then this would fire.
return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
}
// If we got this far then the data is valid.
return true;
}
/**
* Our sanitization callback for `my-arg` parameter.
*
* @param mixed $value Value of the my-arg parameter.
* @param WP_REST_Request $request Current request object.
* @param string $param The name of the parameter in this case, 'my-arg'.
* @return mixed|WP_Error The sanitize value, or a WP_Error if the data could not be sanitized.
*/
function prefix_sanitize_my_arg( $value, $request, $param ) {
$attributes = $request->get_attributes();
if ( isset( $attributes['args'][ $param ] ) ) {
$argument = $attributes['args'][ $param ];
// Check to make sure our argument is a string.
if ( 'string' === $argument['type'] ) {
return sanitize_text_field( $value );
}
} else {
// This code won't execute because we have specified this argument as required.
// If we reused this validation callback and did not have required args then this would fire.
return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
}
// If we got this far then something went wrong don't use user input.
return new WP_Error( 'rest_api_sad', esc_html__( 'Something went terribly wrong.', 'my-textdomain' ), array( 'status' => 500 ) );
}
上記の例では、'my-arg'
という名前の使用を抽象化しました。指定したスキーマのために文字列であるべき他の引数に対して、これらの検証およびサニタイズ関数を使用できます。コードベースとエンドポイントが成長するにつれて、スキーマはコードを軽量で保守可能に保つのに役立ちます。スキーマがない場合でも、検証とサニタイズは可能ですが、どの関数が何を検証すべきかを追跡するのが難しくなります。リクエスト引数にスキーマを追加することで、引数スキーマをクライアントに公開できるため、クライアント側で検証ライブラリを構築でき、無効なリクエストがAPIに送信されるのを防ぐことでパフォーマンスを向上させることができます。
スキーマの使用に不安がある場合でも、各引数に対して検証/サニタイズコールバックを持つことは可能であり、場合によってはカスタム検証を行うのが最も理にかなっていることもあります。
要約
スキーマは時には馬鹿げているように見え、不要な作業のように思えるかもしれませんが、保守可能で発見可能で簡単に拡張可能なエンドポイントを望むのであれば、スキーマを使用することは不可欠です。スキーマはまた、人間とコンピュータの両方に対してエンドポイントを自己文書化するのに役立ちます!
JSONスキーマの基本
WordPressは、JSONスキーマバージョン4仕様のサブセットを使用するバリデーターを実装しています。RFCは、JSONスキーマがどのように機能するかをより深く理解するための推奨読書ですが、この記事ではJSONスキーマの基本とWordPressがサポートする機能について説明します。
API
REST APIは、JSONスキーマを使用するための2つの主要な関数を定義しています:rest_validate_value_from_schema
とrest_sanitize_value_from_schema
。両方の関数は、リクエストデータを最初のパラメータとして受け取り、パラメータのスキーマ定義を2番目のパラメータとして受け取り、オプションでパラメータの名前を3番目のパラメータとして受け取ります。validate関数は、データがスキーマに対して正常に検証されるかどうかに応じて、true
またはWP_Error
インスタンスを返します。sanitize関数は、関数に渡されたデータのサニタイズされた形式を返すか、安全にサニタイズできない場合はWP_Error
インスタンスを返します。
これらの関数を呼び出す際には、常に最初にrest_validate_value_from_schema
を使用してデータを検証し、その関数がtrue
を返した場合にのみ、rest_sanitize_value_from_schema
を使用してデータをサニタイズするように注意してください。両方を使用しないと、エンドポイントがセキュリティの脆弱性にさらされる可能性があります。
エンドポイントがWP_REST_Controller
のサブクラスを使用して実装されている場合、WP_REST_Controller::get_endpoint_args_for_item_schema
メソッドは自動的に引数を組み込みの検証およびサニタイズコールバックを使用するようにマークします。そのため、コールバックを手動で指定する必要はありません。
エンドポイントがコントローラークラスパターンに従っていない場合、WP_REST_Controller::get_collection_params()
から返された引数や、コールバックが指定されていない他のインスタンスでは、WP_REST_Request
オブジェクトがrest_parse_request_arg
関数を使用してサニタイズと検証を適用します。重要なのは、sanitize_callback
が定義されていない場合にのみ適用されることです。そのため、引数定義にカスタムsanitize_callback
を指定した場合、組み込みのJSONスキーマ検証は適用されません。この検証が必要な場合は、引数定義でrest_validate_request_arg
をvalidate_callback
として手動で指定する必要があります。
スキーマのキャッシュ
スキーマは複雑であり、生成に時間がかかることがあります。プラグインのカスタムエンドポイントで生成されたスキーマをキャッシュすることを検討して、同じスキーマオブジェクトを繰り返し生成するのを避けるべきです。
エンドポイントをWP_REST_Controller
のサブクラスを使用して定義している場合、次のようになります:
/**
* Retrieves the attachment's schema, conforming to JSON Schema.
*
* @return array Item schema as an array.
*/
public function get_item_schema() {
// Returned cached copy whenever available.
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$schema = parent::get_item_schema();
// Add endpoint-specific properties to Schema.
$schema['properties']['field_name'] = array( /* ... */ );
$schema['properties']['etcetera'] = array( /* ... */ );
// Cache generated schema on endpoint instance.
$this->schema = $schema;
return $this->add_additional_fields_schema( $this->schema );
}
このパターンは、WordPressコアのバージョン5.3で導入され、#47871で、いくつかのAPIレスポンスを生成する際に最大40%の速度向上をもたらしました。
スキーマドキュメント
基本的なスキーマドキュメントは、いくつかのプロパティで構成されています。
$schema
ドキュメントが使用している仕様のバージョンを説明するメタスキーマへの参照。title
スキーマのタイトル。通常、これは人間が読みやすいラベルですが、WordPressでは機械が読みやすい文字列です。たとえば、投稿エンドポイントのタイトルは「post」です。コメントのタイトルは「comment」です。type
これは、記述されている値の型を指します。これは、7つのプリミティブ型のいずれかである可能性があります。WordPressでは、最上位の型はほぼ常にobject
であり、オブジェクトの配列を返すコレクションエンドポイントでも同様です。properties
オブジェクトに含まれる既知のプロパティのリストとその定義。各プロパティ定義自体もスキーマですが、$schemaの最上位プロパティがないため、より正確にはサブスキーマと呼ばれます。
プリミティブ型
JSONスキーマは、7つの許可されたプリミティブ型のリストを定義しています。
string
文字列値。null
null
値。number
任意の数。小数点も許可されます。PHPのfloat
に相当。integer
数字ですが、小数点や指数は許可されません。boolean
true
またはfalse
値。array
値のリスト。これはJavaScript配列に相当します。PHPでは、これは数値配列または定義されたキーのない配列と呼ばれます。object
キーと値のマップ。これはJavaScriptオブジェクトに相当します。PHPでは、これは連想配列または定義されたキーのある配列と呼ばれます。
値のプリミティブ型は、type
キーワードを使用して指定されます。たとえば、これはJSONスキーマでstring
値を定義する方法です。
array(
'type' => 'string',
);
JSONスキーマは、複数の型を持つ値を定義することを許可します。たとえば、これはstring
またはboolean
のいずれかである値を定義する方法です。
array(
'type' => array( 'boolean', 'string' ),
);
型のジャグリング
WordPress REST APIは、URLフォームエンコードデータをPOST
ボディまたはURLのクエリ部分の両方で受け入れるため、多くのプリミティブ型は、これらの文字列値を適切なネイティブ型に変換するために型のジャグリングを行います。
string
is_string
に従って、文字列のみが許可されます。null
適切に型付けされたnull
のみが受け入れられます。これは、URLまたはURLフォームエンコードされた投稿ボディにnull値を送信することができないことを意味します。JSONリクエストボディを使用する必要があります。number
浮動小数点数、整数、およびis_numeric
を通過する文字列が許可されます。値はfloat
にキャストされます。integer
整数またはfloat
にキャストできる文字列で、分数部分が0に相当します。boolean
ブール値、整数0
および1
、または文字列"0"
、"1"
、"false"
、および"true"
。0
はfalse
として扱われ、1
はtrue
として扱われます。array
wp_is_numeric_array
に従った数値配列または文字列。文字列がカンマ区切りの場合は配列に分割され、それ以外の場合は文字列値を含む配列になります。たとえば、"red,yellow"
はarray( "red", "yellow" )
になり、"blue"
はarray( "blue" )
になります。object
配列、stdClass
オブジェクト、JsonSerializable
を実装するオブジェクト、または空の文字列。値はネイティブPHP配列に変換されます。
複数の型を使用する場合、型は指定された順序で評価されます。これは、REST APIエンドポイントで受信したサニタイズされたデータに影響を与える可能性があります。たとえば、前の例で、送信された値が"1"
の場合、それはブール値true
にサニタイズされます。しかし、順序が反転した場合、値は文字列"1"
のままになります。
JSONスキーマ仕様は、type
フィールドなしでスキーマを定義することを許可します。しかし、WordPressの実装では、type
を定義する必要があり、型が省略された場合は_doing_it_wrong
通知が発行されます。
フォーマット
7つのプリミティブ型は単なるプリミティブですが、より複雑な値の型をどのように定義しますか?その方法の1つは、format
キーワードを使用することです。format
キーワードは、明確に定義された構造を持つ値の追加の意味レベルの検証を定義することを可能にします。
たとえば、日付値を必要とする場合は、date-time
フォーマットを使用します。
array(
'type' => 'string',
'format' => 'date-time',
);
WordPressは次のフォーマットをサポートしています:
date-time
RFC3339に従った日付。uri
esc_url_raw
に従ったURI。email
is_email
に従ったメールアドレス。ip
v4またはv6のIPアドレス。uuid
任意のバージョンのUUID。hex-color
先頭に#
を持つ3または6文字の16進数カラー。
値は、空の文字列であっても、そのフォーマットに一致する必要があります。「空」の値を許可することが望ましい場合は、null
を可能な型として追加してください。
たとえば、次のスキーマは127.0.0.1
またはnull
を可能な値として許可します。
array(
'type' => array( 'string', 'null' ),
'format' => 'ip',
);
文字列
#### minLengthとmaxLength
`````minLength`````および`````maxLength`````キーワードは、文字列の許容される長さを制約するために使用できます。重要なことに、マルチバイト文字は1つの文字としてカウントされ、境界は含まれます。
たとえば、次のスキーマでは、`````ab`````、`````abc`````、`````abcd`````が有効であり、`````a`````および`````abcde`````は無効です。
``````bash
array(
'type' => 'string',
'minLength' => 2,
'maxLength' => 4,
);
`
#### パターン
JSONスキーマキーワード`````pattern`````は、文字列フィールドが正規表現に一致することを検証するために使用できます。
たとえば、次のスキーマでは、`````#123`````が有効ですが、`````#abc`````は無効です。
``````bash
array(
'type' => 'string',
'pattern' => '#[0-9]+',
);
`
正規表現は自動的にアンカーされません。正規表現フラグ、たとえば/i
は、大文字と小文字を区別しない一致を作成するためにサポートされていません。
JSONスキーマRFCは、スキーマができるだけ多くの異なるプログラミング言語間で相互運用可能であるように、次の正規表現機能に制限することを推奨しています。
- JSON仕様[RFC4627]で定義された個々のUnicode文字。
- 単純な文字クラス
[abc]
、範囲文字クラス[a-z]
。 - 補完された文字クラス
[^abc]
、[^a-z]
。 - 単純な量指定子:
+
(1回以上)、*
(0回以上)、?
(0回または1回)、およびその遅延バージョン+?
、*?
、??
。 - 範囲量指定子:
{x}
(正確にx回)、{x,y}
(少なくともx、最大y回)、{x,}
(x回以上)、およびその遅延バージョン。 - 入力の開始
^
および入力の終了$
アンカー。 - 単純なグルーピング
(...)
および選択|
。
パターンは、ECMA 262正規表現ダイアレクトに従って有効である必要があります。
数値
#### minimumとmaximum
`````minimum`````および`````maximum`````キーワードは、許容される数値の範囲を制約することを可能にします。たとえば、`````2`````はこのスキーマに従って有効ですが、`````0`````および`````4`````は無効です。
``````bash
array(
'type' => 'integer',
'minimum' => 1,
'maximum' => 3,
);
`
JSONスキーマはまた、exclusiveMinimum
およびexclusiveMaximum
キーワードを使用して、値がそれぞれ定義されたminimum
またはmaximum
に等しくないことを示すことを許可します。たとえば、この場合、2
のみが許容される値になります。
array(
'type' => 'integer',
'minimum' => 1,
'exclusiveMinimum' => true,
'maximum' => 3,
'exclusiveMaximum' => true,
);
multipleOf
``````bash
array(
'type' => 'integer',
'multipleOf' => 2,
);
`
``````bash
array(
'type' => 'number',
'minimum' => 0,
'maximum' => 100,
'multipleOf' => 0.1,
);
`
配列
たとえば、次のスキーマはIPアドレスの配列を要求します。
``````bash
array(
'type' => 'array',
'items' => array(
'type' => 'string',
'format' => 'ip',
),
);
`
これは検証に合格します。
[ "127.0.0.1", "255.255.255.255" ]
これは検証に失敗します。
[ "127.0.0.1", 5 ]
``````bash
array(
'type' => 'array',
'items' => array(
'type' => 'array',
'items' => array(
'type' => 'string',
'format' => 'hex-color',
),
),
);
`
これは検証に合格します。
[
[ "#ff6d69", "#fecc50" ],
[ "#0be7fb" ]
]
これは検証に失敗します。
[
[ "#ff6d69", "#fecc50" ],
"george"
]
minItemsとmaxItems
たとえば、次のスキーマでは、`````[ "a" ]`````および`````[ "a", "b" ]`````が有効であり、`````[]`````および`````[ "a", "b", "c" ]`````が無効です。
``````bash
array(
'type' => 'array',
'minItems' => 1,
'maxItems' => 2,
'items' => array(
'type' => 'string',
),
);
`
#### uniqueItems
`````uniqueItems`````キーワードは、配列内のすべてのアイテムがユニークであることを要求するために使用できます。
たとえば、次のスキーマでは、`````[ "a", "b" ]`````が有効ですが、`````[ "a", "a" ]`````は無効です。
``````bash
array(
'type' => 'array',
'uniqueItems' => true,
'items' => array(
'type' => 'string',
),
);
`
ユニーク性
異なる型のアイテムはユニークと見なされます。たとえば、"1"
、1
、および1.0
は異なる値です。
配列が比較されると、アイテムの順序が重要です。したがって、与えられた配列はすべてユニークなアイテムを持つと見なされます。
[
[ "a", "b" ],
[ "b", "a" ]
]
オブジェクトが比較されると、メンバーが出現する順序は重要ではありません。したがって、与えられた配列は、値が同じであるため、重複アイテムを持つと見なされます。値は異なる順序で出現するだけです。
[
{
"a": 1,
"b": 2
},
{
"b": 2,
"a": 1
}
]
ユニーク性は、rest_validate_value_from_schema
およびrest_sanitize_value_from_schema
の両方でチェックされます。これは、アイテムがサニタイズが適用される前にユニークと見なされる場合でも、サニタイズ後にアイテムが同一の値に収束するのを防ぐためです。
次のスキーマを考えてみましょう:
array(
'type' => 'array',
'uniqueItems' => true,
'items' => array(
'type' => 'string',
'format' => 'uri',
),
);
[ "https://example.org/hello world", "https://example.org/hello%20world" ]
を持つリクエストは、各文字列値が異なるため、検証に合格します。しかし、esc_url_raw
が最初のURLのスペースを%20
に変換した後、値は同一になります。
この場合、rest_sanitize_value_from_schema
はエラーを返します。そのため、常にパラメータを検証およびサニタイズする必要があります。
オブジェクト
たとえば、次のスキーマは、プロパティ`````name`````が文字列であり、`````color`````が16進数カラーであるオブジェクトを要求します。
``````bash
array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
),
'color' => array(
'type' => 'string',
'format' => 'hex-color',
),
),
);
`
これは検証に合格します。
{
"name": "Primary",
"color": "#ff6d69"
}
これは検証に失敗します。
{
"name": "Primary",
"color": "orange"
}
必須プロパティ
デフォルトでは、オブジェクトにリストされたプロパティはすべてオプションであるため、予期しないかもしれませんが、次のようなものも前のスキーマの検証に合格します。
{
"name": "Primary"
}
プロパティを提供することを要求するための2つのメカニズムがあります。
バージョン3構文
WordPressは主にJSONスキーマバージョン4に従っていますが、1つの方法は、必須プロパティを定義するための構文です。主な方法は、各プロパティの定義にrequired
キーワードを追加することによるJSONスキーマバージョン3構文を使用することです。
array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
'required' => true,
),
'color' => array(
'type' => 'string',
'format' => 'hex-color',
'required' => true,
),
),
);
バージョン4構文
WordPressは、オブジェクトの必須プロパティのリストがプロパティ名の配列として定義されるJSONスキーマバージョン4の必須プロパティ構文もサポートしています。これは、メタ値が必須プロパティのリストを持つことを指定する際に特に便利です。
次のメタフィールドを考えてみましょう。
register_post_meta( 'post', 'fixed_in', array(
'type' => 'object',
'show_in_rest' => array(
'single' => true,
'schema' => array(
'required' => array( 'revision', 'version' ),
'type' => 'object',
'properties' => array(
'revision' => array(
'type' => 'integer',
),
'version' => array(
'type' => 'string',
),
),
),
),
) );
次のリクエストは検証に失敗します。
{
"title": "Check required properties",
"content": "We should check that required properties are provided",
"meta": {
"fixed_in": {
"revision": 47089
}
}
}
バージョン4構文は、`````WP_REST_Controller::get_item_schema()`````のエンドポイントの最上位スキーマにはサポートされていません。次のスキーマでは、ユーザーはタイトルやコンテンツプロパティなしでリクエストを正常に送信できます。これは、スキーマドキュメント自体が検証に使用されるのではなく、パラメータ定義のリストに変換されるためです。
``````bash
array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'my-endpoint',
'type' => 'object',
'required' => array( 'title', 'content' ),
'properties' => array(
'title' => array(
'type' => 'string',
),
'content' => array(
'type' => 'string',
),
),
);
`
additionalProperties
おそらく直感に反して、デフォルトではJSONスキーマは、スキーマに指定されていない追加のプロパティを提供することも許可します。そのため、次のようなものも検証に合格します。
{
"name": "Primary",
"color": "#ff6d69",
"description": "The primary color to use in the theme."
}
これは、additionalProperties
キーワードを使用してカスタマイズできます。additionalProperties
をfalseに設定すると、未知のプロパティを持つデータは拒否されます。
array(
'type' => 'object',
'additionalProperties' => false,
'properties' => array(
'name' => array(
'type' => 'string',
),
'color' => array(
'type' => 'string',
'format' => 'hex-color',
),
),
);
これが役立つ方法の1つは、各値が独自のキーを持つリストを受け入れたい場合です。たとえば:
``````bash
array(
'type' => 'object',
'properties' => array(),
'additionalProperties' => array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
'required' => true,
),
'color' => array(
'type' => 'string',
'format' => 'hex-color',
'required' => true,
),
),
),
);
`
これは検証に合格します。
{
"primary": {
"name": "Primary",
"color": "#ff6d69"
},
"secondary": {
"name": "Secondary",
"color": "#fecc50"
}
}
これは検証に失敗します。
{
"primary": {
"name": "Primary",
"color": "#ff6d69"
},
"secondary": "#fecc50"
}
patternProperties
たとえば、このスキーマは、各値が16進数カラーであり、プロパティが「単語」文字のみを含むことを要求します。
``````bash
array(
'type' => 'object',
'patternProperties' => array(
'^\\w+$' => array(
'type' => 'string',
'format' => 'hex-color',
),
),
'additionalProperties' => false,
);
`
これは検証に合格します。
{
"primary": "#ff6d69",
"secondary": "#fecc50"
}
これは検証に失敗します。
{
"primary": "blue",
"$secondary": "#fecc50"
}
REST APIがpatternProperties
スキーマを検証する際、プロパティがパターンに一致しない場合、そのプロパティは許可され、内容に対して検証が適用されません。この論理が望ましくない場合は、スキーマにadditionalProperties
を追加して、一致しないプロパティを許可しないようにします。
minPropertiesとmaxProperties
``````bash
array(
'type' => 'object',
'additionalProperties' => array(
'type' => 'string',
'format' => 'hex-color',
),
'minProperties' => 1,
'maxProperties' => 3,
);
`
これは検証に合格します。
{
"primary": "#52accc",
"secondary": "#096484"
}
これは検証に失敗します。
{
"primary": "#52accc",
"secondary": "#096484",
"tertiary": "#07526c"
}
<a name="type-agnostic-keywords"></a>
### 型に依存しないキーワード
#### oneOfとanyOf
これらは、JSONスキーマバリデーターが値を検証する際に使用するスキーマの1つを選択できるようにする高度なキーワードです。`````anyOf`````キーワードは、値が指定されたスキーマのいずれかに一致することを許可します。一方、`````oneOf`````キーワードは、値が*正確に*1つのスキーマに一致することを要求します。
たとえば、このスキーマは、エンドポイントに「操作」の配列を送信することを許可します。各操作は「クロップ」または「回転」である可能性があります。
``````bash
array(
'type' => 'array',
'items' => array(
'oneOf' => array(
array(
'title' => 'Crop',
'type' => 'object',
'properties' => array(
'operation' => array(
'type' => 'string',
'enum' => array(
'crop',
),
),
'x' => array(
'type' => 'integer',
),
'y' => array(
'type' => 'integer',
),
),
),
array(
'title' => 'Rotation',
'type' => 'object',
'properties' => array(
'operation' => array(
'type' => 'string',
'enum' => array(
'rotate',
),
),
'degrees' => array(
'type' => 'integer',
'minimum' => 0,
'maximum' => 360,
),
),
),
),
),
);
`
REST APIは、oneOf
配列に指定された各スキーマをループし、一致を探します。正確に1つのスキーマが一致する場合、検証は成功します。複数のスキーマが一致する場合、検証は失敗します。一致するスキーマがない場合、バリデーターは最も近い一致するスキーマを見つけて、適切なエラーメッセージを返します。
operations[0]は有効な回転ではありません。理由:operations[0][degrees]は0(含む)から360(含む)の間でなければなりません。
より役立つエラーメッセージを生成するために、各oneOf
またはanyOf
スキーマにtitle
プロパティを付与することを強く推奨します。
変更履歴
WordPress 5.6
multipleOf
JSONスキーマキーワードをサポートします。r49063minProperties
およびmaxProperties
JSONスキーマキーワードをサポートします。r49053patternProperties
JSONスキーマキーワードをサポートします。r49082anyOf
およびoneOf
JSONスキーマキーワードをサポートします。r49246
WordPress 5.5
- マルチタイプJSONスキーマサポートを改善しました。r48306
- オブジェクトを検証する際に必須プロパティが提供されているかを確認します。r47809
format
キーワードをtype
がstring
の場合にのみ検証します。r48300uuid
JSONスキーマフォーマットをサポートします。47753hex-color
JSONスキーマフォーマットをサポートします。r47450pattern
JSONスキーマキーワードをサポートします。r47810minItems
、maxItems
、およびuniqueItems
JSONスキーマキーワードをサポートします。r47923 r48357minLength
およびmaxLength
JSONスキーマキーワードをサポートします。r47627
WordPress 5.4
- 空の文字列を空のオブジェクトに型ジャグリングすることをサポートします。r47362
WordPress 5.3
WordPress 4.9
object
プリミティブ型をサポートします。r41727