概要

スキーマは、データがどのように構造化されているかを示すメタデータです。ほとんどのデータベースは、データをより構造的に考えることを可能にするスキーマの何らかの形式を実装しています。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データの塊を見てみましょう。

  1. {
  2. "shouldBeArray": 'LOL definitely not an array',
  3. "shouldBeInteger": ['lolz', 'you', 'need', 'schema'],
  4. "shouldBeString": 123456789
  5. }

JSONパーサーは、そのデータを問題なく処理し、何も文句を言いません。なぜなら、それは有効なJSONだからです。クライアントとサーバーはデータについて何も知らず、何を期待するかもわかりません。ただJSONを見るだけです。スキーマを実装することで、実際にコードベースを簡素化できます。スキーマはデータをより良く構造化するのに役立ち、アプリケーションがWordPress REST APIとの相互作用をより簡単に考えることができるようになります。WordPress REST APIはスキーマの使用を強制しませんが、推奨されています。スキーマデータはAPIに組み込まれる方法が2つあります;リソースのスキーマと登録された引数のスキーマです。

リソーススキーマ

リソースのスキーマは、特定のオブジェクトにどのフィールドが存在するかを示します。ルートを登録する際に、ルートのリソーススキーマも指定できます。JSONスキーマのPHP表現におけるシンプルなコメントスキーマがどのように見えるかを見てみましょう。

  1. // Register our routes.
  2. function prefix_register_my_comment_route() {
  3. register_rest_route( 'my-namespace/v1', '/comments', array(
  4. // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
  5. array(
  6. 'methods' => 'GET',
  7. 'callback' => 'prefix_get_comment_sample',
  8. ),
  9. // Register our schema callback.
  10. 'schema' => 'prefix_get_comment_schema',
  11. ) );
  12. }
  13. add_action( 'rest_api_init', 'prefix_register_my_comment_route' );
  14. /**
  15. * Grabs the five most recent comments and outputs them as a rest response.
  16. *
  17. * @param WP_REST_Request $request Current request.
  18. */
  19. function prefix_get_comment_sample( $request ) {
  20. $args = array(
  21. 'number' => 5,
  22. );
  23. $comments = get_comments( $args );
  24. $data = array();
  25. if ( empty( $comments ) ) {
  26. return rest_ensure_response( $data );
  27. }
  28. foreach ( $comments as $comment ) {
  29. $response = prefix_rest_prepare_comment( $comment, $request );
  30. $data[] = prefix_prepare_for_collection( $response );
  31. }
  32. // Return all of our comment response data.
  33. return rest_ensure_response( $data );
  34. }
  35. /**
  36. * Matches the comment data to the schema we want.
  37. *
  38. * @param WP_Comment $comment The comment object whose response is being prepared.
  39. */
  40. function prefix_rest_prepare_comment( $comment, $request ) {
  41. $comment_data = array();
  42. $schema = prefix_get_comment_schema();
  43. // We are also renaming the fields to more understandable names.
  44. if ( isset( $schema['properties']['id'] ) ) {
  45. $comment_data['id'] = (int) $comment->comment_ID;
  46. }
  47. if ( isset( $schema['properties']['author'] ) ) {
  48. $comment_data['author'] = (int) $comment->user_id;
  49. }
  50. if ( isset( $schema['properties']['content'] ) ) {
  51. $comment_data['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
  52. }
  53. return rest_ensure_response( $comment_data );
  54. }
  55. /**
  56. * Prepare a response for inserting into a collection of responses.
  57. *
  58. * This is copied from WP_REST_Controller class in the WP REST API v2 plugin.
  59. *
  60. * @param WP_REST_Response $response Response object.
  61. * @return array Response data, ready for insertion into collection data.
  62. */
  63. function prefix_prepare_for_collection( $response ) {
  64. if ( ! ( $response instanceof WP_REST_Response ) ) {
  65. return $response;
  66. }
  67. $data = (array) $response->get_data();
  68. $links = rest_get_server()::get_compact_response_links( $response );
  69. if ( ! empty( $links ) ) {
  70. $data['_links'] = $links;
  71. }
  72. return $data;
  73. }
  74. /**
  75. * Get our sample schema for comments.
  76. */
  77. function prefix_get_comment_schema() {
  78. $schema = array(
  79. // This tells the spec of JSON Schema we are using which is draft 4.
  80. '$schema' => 'http://json-schema.org/draft-04/schema#',
  81. // The title property marks the identity of the resource.
  82. 'title' => 'comment',
  83. 'type' => 'object',
  84. // In JSON Schema you can specify object properties in the properties attribute.
  85. 'properties' => array(
  86. 'id' => array(
  87. 'description' => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
  88. 'type' => 'integer',
  89. 'context' => array( 'view', 'edit', 'embed' ),
  90. 'readonly' => true,
  91. ),
  92. 'author' => array(
  93. 'description' => esc_html__( 'The id of the user object, if author was a user.', 'my-textdomain' ),
  94. 'type' => 'integer',
  95. ),
  96. 'content' => array(
  97. 'description' => esc_html__( 'The content for the object.', 'my-textdomain' ),
  98. 'type' => 'string',
  99. ),
  100. ),
  101. );
  102. return $schema;
  103. }

各コメントリソースが、指定したスキーマに一致していることに気づくでしょう。この変更は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 viaOPTIONS`リクエストに行うと、リソーススキーマを公開することは、スキーマパズルの一部に過ぎません。登録された引数のためにもスキーマを使用したいと考えています。

引数スキーマ

エンドポイントのリクエスト引数を登録する際に、JSONスキーマを使用して引数がどのようなものであるべきかのデータを提供することもできます。これにより、エンドポイントが拡張されるにつれて再利用可能な検証ライブラリを書くことが可能になります。スキーマは最初は手間がかかりますが、成長するプロダクションアプリケーションを書くつもりなら、スキーマの使用を検討すべきです。引数スキーマと検証の使用例を見てみましょう。

  1. // Register our routes.
  2. function prefix_register_my_arg_route() {
  3. register_rest_route( 'my-namespace/v1', '/schema-arg', array(
  4. // Here we register our endpoint.
  5. array(
  6. 'methods' => 'GET',
  7. 'callback' => 'prefix_get_item',
  8. 'args' => prefix_get_endpoint_args(),
  9. ),
  10. ) );
  11. }
  12. // Hook registration into 'rest_api_init' hook.
  13. add_action( 'rest_api_init', 'prefix_register_my_arg_route' );
  14. /**
  15. * Returns the request argument `my-arg` as a rest response.
  16. *
  17. * @param WP_REST_Request $request Current request.
  18. */
  19. function prefix_get_item( $request ) {
  20. // If we didn't use required in the schema this would throw an error when my arg is not set.
  21. return rest_ensure_response( $request['my-arg'] );
  22. }
  23. /**
  24. * Get the argument schema for this example endpoint.
  25. */
  26. function prefix_get_endpoint_args() {
  27. $args = array();
  28. // Here we add our PHP representation of JSON Schema.
  29. $args['my-arg'] = array(
  30. 'description' => esc_html__( 'This is the argument our endpoint returns.', 'my-textdomain' ),
  31. 'type' => 'string',
  32. 'validate_callback' => 'prefix_validate_my_arg',
  33. 'sanitize_callback' => 'prefix_sanitize_my_arg',
  34. 'required' => true,
  35. );
  36. return $args;
  37. }
  38. /**
  39. * Our validation callback for `my-arg` parameter.
  40. *
  41. * @param mixed $value Value of the my-arg parameter.
  42. * @param WP_REST_Request $request Current request object.
  43. * @param string $param The name of the parameter in this case, 'my-arg'.
  44. * @return true|WP_Error True if the data is valid, WP_Error otherwise.
  45. */
  46. function prefix_validate_my_arg( $value, $request, $param ) {
  47. $attributes = $request->get_attributes();
  48. if ( isset( $attributes['args'][ $param ] ) ) {
  49. $argument = $attributes['args'][ $param ];
  50. // Check to make sure our argument is a string.
  51. if ( 'string' === $argument['type'] && ! is_string( $value ) ) {
  52. 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 ) );
  53. }
  54. } else {
  55. // This code won't execute because we have specified this argument as required.
  56. // If we reused this validation callback and did not have required args then this would fire.
  57. return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
  58. }
  59. // If we got this far then the data is valid.
  60. return true;
  61. }
  62. /**
  63. * Our sanitization callback for `my-arg` parameter.
  64. *
  65. * @param mixed $value Value of the my-arg parameter.
  66. * @param WP_REST_Request $request Current request object.
  67. * @param string $param The name of the parameter in this case, 'my-arg'.
  68. * @return mixed|WP_Error The sanitize value, or a WP_Error if the data could not be sanitized.
  69. */
  70. function prefix_sanitize_my_arg( $value, $request, $param ) {
  71. $attributes = $request->get_attributes();
  72. if ( isset( $attributes['args'][ $param ] ) ) {
  73. $argument = $attributes['args'][ $param ];
  74. // Check to make sure our argument is a string.
  75. if ( 'string' === $argument['type'] ) {
  76. return sanitize_text_field( $value );
  77. }
  78. } else {
  79. // This code won't execute because we have specified this argument as required.
  80. // If we reused this validation callback and did not have required args then this would fire.
  81. return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( '%s was not registered as a request argument.', 'my-textdomain' ), $param ), array( 'status' => 400 ) );
  82. }
  83. // If we got this far then something went wrong don't use user input.
  84. return new WP_Error( 'rest_api_sad', esc_html__( 'Something went terribly wrong.', 'my-textdomain' ), array( 'status' => 500 ) );
  85. }

上記の例では、'my-arg'という名前の使用を抽象化しました。指定したスキーマのために文字列であるべき他の引数に対して、これらの検証およびサニタイズ関数を使用できます。コードベースとエンドポイントが成長するにつれて、スキーマはコードを軽量で保守可能に保つのに役立ちます。スキーマがない場合でも、検証とサニタイズは可能ですが、どの関数が何を検証すべきかを追跡するのが難しくなります。リクエスト引数にスキーマを追加することで、引数スキーマをクライアントに公開できるため、クライアント側で検証ライブラリを構築でき、無効なリクエストがAPIに送信されるのを防ぐことでパフォーマンスを向上させることができます。

スキーマの使用に不安がある場合でも、各引数に対して検証/サニタイズコールバックを持つことは可能であり、場合によってはカスタム検証を行うのが最も理にかなっていることもあります。

要約

スキーマは時には馬鹿げているように見え、不要な作業のように思えるかもしれませんが、保守可能で発見可能で簡単に拡張可能なエンドポイントを望むのであれば、スキーマを使用することは不可欠です。スキーマはまた、人間とコンピュータの両方に対してエンドポイントを自己文書化するのに役立ちます!

JSONスキーマの基本

WordPressは、JSONスキーマバージョン4仕様のサブセットを使用するバリデーターを実装しています。RFCは、JSONスキーマがどのように機能するかをより深く理解するための推奨読書ですが、この記事ではJSONスキーマの基本とWordPressがサポートする機能について説明します。

API

REST APIは、JSONスキーマを使用するための2つの主要な関数を定義しています:rest_validate_value_from_schemarest_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_argvalidate_callbackとして手動で指定する必要があります。

スキーマのキャッシュ

スキーマは複雑であり、生成に時間がかかることがあります。プラグインのカスタムエンドポイントで生成されたスキーマをキャッシュすることを検討して、同じスキーマオブジェクトを繰り返し生成するのを避けるべきです。

エンドポイントをWP_REST_Controllerのサブクラスを使用して定義している場合、次のようになります:

  1. /**
  2. * Retrieves the attachment's schema, conforming to JSON Schema.
  3. *
  4. * @return array Item schema as an array.
  5. */
  6. public function get_item_schema() {
  7. // Returned cached copy whenever available.
  8. if ( $this->schema ) {
  9. return $this->add_additional_fields_schema( $this->schema );
  10. }
  11. $schema = parent::get_item_schema();
  12. // Add endpoint-specific properties to Schema.
  13. $schema['properties']['field_name'] = array( /* ... */ );
  14. $schema['properties']['etcetera'] = array( /* ... */ );
  15. // Cache generated schema on endpoint instance.
  16. $this->schema = $schema;
  17. return $this->add_additional_fields_schema( $this->schema );
  18. }

このパターンは、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値を定義する方法です。

  1. array(
  2. 'type' => 'string',
  3. );

JSONスキーマは、複数の型を持つ値を定義することを許可します。たとえば、これはstringまたはbooleanのいずれかである値を定義する方法です。

  1. array(
  2. 'type' => array( 'boolean', 'string' ),
  3. );

型のジャグリング

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"0falseとして扱われ、1trueとして扱われます。
  • 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フォーマットを使用します。

  1. array(
  2. 'type' => 'string',
  3. 'format' => 'date-time',
  4. );

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を可能な値として許可します。

  1. array(
  2. 'type' => array( 'string', 'null' ),
  3. 'format' => 'ip',
  4. );

文字列

  1. #### minLengthとmaxLength
  2. `````minLength`````および`````maxLength`````キーワードは、文字列の許容される長さを制約するために使用できます。重要なことに、マルチバイト文字は1つの文字としてカウントされ、境界は含まれます。
  3. たとえば、次のスキーマでは、`````ab``````````abc``````````abcd`````が有効であり、`````a`````および`````abcde`````は無効です。
  4. ``````bash
  5. array(
  6. 'type' => 'string',
  7. 'minLength' => 2,
  8. 'maxLength' => 4,
  9. );
  10. `
  1. #### パターン
  2. JSONスキーマキーワード`````pattern`````は、文字列フィールドが正規表現に一致することを検証するために使用できます。
  3. たとえば、次のスキーマでは、`````#123`````が有効ですが、`````#abc`````は無効です。
  4. ``````bash
  5. array(
  6. 'type' => 'string',
  7. 'pattern' => '#[0-9]+',
  8. );
  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正規表現ダイアレクトに従って有効である必要があります。

数値

  1. #### minimumとmaximum
  2. `````minimum`````および`````maximum`````キーワードは、許容される数値の範囲を制約することを可能にします。たとえば、`````2`````はこのスキーマに従って有効ですが、`````0`````および`````4`````は無効です。
  3. ``````bash
  4. array(
  5. 'type' => 'integer',
  6. 'minimum' => 1,
  7. 'maximum' => 3,
  8. );
  9. `

JSONスキーマはまた、exclusiveMinimumおよびexclusiveMaximumキーワードを使用して、値がそれぞれ定義されたminimumまたはmaximumに等しくないことを示すことを許可します。たとえば、この場合、2のみが許容される値になります。

  1. array(
  2. 'type' => 'integer',
  3. 'minimum' => 1,
  4. 'exclusiveMinimum' => true,
  5. 'maximum' => 3,
  6. 'exclusiveMaximum' => true,
  7. );

multipleOf

  1. ``````bash
  2. array(
  3. 'type' => 'integer',
  4. 'multipleOf' => 2,
  5. );
  6. `
  1. ``````bash
  2. array(
  3. 'type' => 'number',
  4. 'minimum' => 0,
  5. 'maximum' => 100,
  6. 'multipleOf' => 0.1,
  7. );
  8. `

配列

  1. たとえば、次のスキーマはIPアドレスの配列を要求します。
  2. ``````bash
  3. array(
  4. 'type' => 'array',
  5. 'items' => array(
  6. 'type' => 'string',
  7. 'format' => 'ip',
  8. ),
  9. );
  10. `

これは検証に合格します。

  1. [ "127.0.0.1", "255.255.255.255" ]

これは検証に失敗します。

  1. [ "127.0.0.1", 5 ]
  1. ``````bash
  2. array(
  3. 'type' => 'array',
  4. 'items' => array(
  5. 'type' => 'array',
  6. 'items' => array(
  7. 'type' => 'string',
  8. 'format' => 'hex-color',
  9. ),
  10. ),
  11. );
  12. `

これは検証に合格します。

  1. [
  2. [ "#ff6d69", "#fecc50" ],
  3. [ "#0be7fb" ]
  4. ]

これは検証に失敗します。

  1. [
  2. [ "#ff6d69", "#fecc50" ],
  3. "george"
  4. ]

minItemsとmaxItems

  1. たとえば、次のスキーマでは、`````[ "a" ]`````および`````[ "a", "b" ]`````が有効であり、`````[]`````および`````[ "a", "b", "c" ]`````が無効です。
  2. ``````bash
  3. array(
  4. 'type' => 'array',
  5. 'minItems' => 1,
  6. 'maxItems' => 2,
  7. 'items' => array(
  8. 'type' => 'string',
  9. ),
  10. );
  11. `
  1. #### uniqueItems
  2. `````uniqueItems`````キーワードは、配列内のすべてのアイテムがユニークであることを要求するために使用できます。
  3. たとえば、次のスキーマでは、`````[ "a", "b" ]`````が有効ですが、`````[ "a", "a" ]`````は無効です。
  4. ``````bash
  5. array(
  6. 'type' => 'array',
  7. 'uniqueItems' => true,
  8. 'items' => array(
  9. 'type' => 'string',
  10. ),
  11. );
  12. `
ユニーク性

異なる型のアイテムはユニークと見なされます。たとえば、"1"1、および1.0は異なる値です。

配列が比較されると、アイテムの順序が重要です。したがって、与えられた配列はすべてユニークなアイテムを持つと見なされます。

  1. [
  2. [ "a", "b" ],
  3. [ "b", "a" ]
  4. ]

オブジェクトが比較されると、メンバーが出現する順序は重要ではありません。したがって、与えられた配列は、値が同じであるため、重複アイテムを持つと見なされます。値は異なる順序で出現するだけです。

  1. [
  2. {
  3. "a": 1,
  4. "b": 2
  5. },
  6. {
  7. "b": 2,
  8. "a": 1
  9. }
  10. ]

ユニーク性は、rest_validate_value_from_schemaおよびrest_sanitize_value_from_schemaの両方でチェックされます。これは、アイテムがサニタイズが適用される前にユニークと見なされる場合でも、サニタイズ後にアイテムが同一の値に収束するのを防ぐためです。

次のスキーマを考えてみましょう:

  1. array(
  2. 'type' => 'array',
  3. 'uniqueItems' => true,
  4. 'items' => array(
  5. 'type' => 'string',
  6. 'format' => 'uri',
  7. ),
  8. );

[ "https://example.org/hello world", "https://example.org/hello%20world" ]を持つリクエストは、各文字列値が異なるため、検証に合格します。しかし、esc_url_rawが最初のURLのスペースを%20に変換した後、値は同一になります。

この場合、rest_sanitize_value_from_schemaはエラーを返します。そのため、常にパラメータを検証およびサニタイズする必要があります。

オブジェクト

  1. たとえば、次のスキーマは、プロパティ`````name`````が文字列であり、`````color`````16進数カラーであるオブジェクトを要求します。
  2. ``````bash
  3. array(
  4. 'type' => 'object',
  5. 'properties' => array(
  6. 'name' => array(
  7. 'type' => 'string',
  8. ),
  9. 'color' => array(
  10. 'type' => 'string',
  11. 'format' => 'hex-color',
  12. ),
  13. ),
  14. );
  15. `

これは検証に合格します。

  1. {
  2. "name": "Primary",
  3. "color": "#ff6d69"
  4. }

これは検証に失敗します。

  1. {
  2. "name": "Primary",
  3. "color": "orange"
  4. }

必須プロパティ

デフォルトでは、オブジェクトにリストされたプロパティはすべてオプションであるため、予期しないかもしれませんが、次のようなものも前のスキーマの検証に合格します。

  1. {
  2. "name": "Primary"
  3. }

プロパティを提供することを要求するための2つのメカニズムがあります。

バージョン3構文

WordPressは主にJSONスキーマバージョン4に従っていますが、1つの方法は、必須プロパティを定義するための構文です。主な方法は、各プロパティの定義にrequiredキーワードを追加することによるJSONスキーマバージョン3構文を使用することです。

  1. array(
  2. 'type' => 'object',
  3. 'properties' => array(
  4. 'name' => array(
  5. 'type' => 'string',
  6. 'required' => true,
  7. ),
  8. 'color' => array(
  9. 'type' => 'string',
  10. 'format' => 'hex-color',
  11. 'required' => true,
  12. ),
  13. ),
  14. );
バージョン4構文

WordPressは、オブジェクトの必須プロパティのリストがプロパティ名の配列として定義されるJSONスキーマバージョン4の必須プロパティ構文もサポートしています。これは、メタ値が必須プロパティのリストを持つことを指定する際に特に便利です。

次のメタフィールドを考えてみましょう。

  1. register_post_meta( 'post', 'fixed_in', array(
  2. 'type' => 'object',
  3. 'show_in_rest' => array(
  4. 'single' => true,
  5. 'schema' => array(
  6. 'required' => array( 'revision', 'version' ),
  7. 'type' => 'object',
  8. 'properties' => array(
  9. 'revision' => array(
  10. 'type' => 'integer',
  11. ),
  12. 'version' => array(
  13. 'type' => 'string',
  14. ),
  15. ),
  16. ),
  17. ),
  18. ) );

次のリクエストは検証に失敗します。

  1. {
  2. "title": "Check required properties",
  3. "content": "We should check that required properties are provided",
  4. "meta": {
  5. "fixed_in": {
  6. "revision": 47089
  7. }
  8. }
  9. }
  1. バージョン4構文は、`````WP_REST_Controller::get_item_schema()`````のエンドポイントの最上位スキーマにはサポートされていません。次のスキーマでは、ユーザーはタイトルやコンテンツプロパティなしでリクエストを正常に送信できます。これは、スキーマドキュメント自体が検証に使用されるのではなく、パラメータ定義のリストに変換されるためです。
  2. ``````bash
  3. array(
  4. '$schema' => 'http://json-schema.org/draft-04/schema#',
  5. 'title' => 'my-endpoint',
  6. 'type' => 'object',
  7. 'required' => array( 'title', 'content' ),
  8. 'properties' => array(
  9. 'title' => array(
  10. 'type' => 'string',
  11. ),
  12. 'content' => array(
  13. 'type' => 'string',
  14. ),
  15. ),
  16. );
  17. `

additionalProperties

おそらく直感に反して、デフォルトではJSONスキーマは、スキーマに指定されていない追加のプロパティを提供することも許可します。そのため、次のようなものも検証に合格します。

  1. {
  2. "name": "Primary",
  3. "color": "#ff6d69",
  4. "description": "The primary color to use in the theme."
  5. }

これは、additionalPropertiesキーワードを使用してカスタマイズできます。additionalPropertiesをfalseに設定すると、未知のプロパティを持つデータは拒否されます。

  1. array(
  2. 'type' => 'object',
  3. 'additionalProperties' => false,
  4. 'properties' => array(
  5. 'name' => array(
  6. 'type' => 'string',
  7. ),
  8. 'color' => array(
  9. 'type' => 'string',
  10. 'format' => 'hex-color',
  11. ),
  12. ),
  13. );
  1. これが役立つ方法の1つは、各値が独自のキーを持つリストを受け入れたい場合です。たとえば:
  2. ``````bash
  3. array(
  4. 'type' => 'object',
  5. 'properties' => array(),
  6. 'additionalProperties' => array(
  7. 'type' => 'object',
  8. 'properties' => array(
  9. 'name' => array(
  10. 'type' => 'string',
  11. 'required' => true,
  12. ),
  13. 'color' => array(
  14. 'type' => 'string',
  15. 'format' => 'hex-color',
  16. 'required' => true,
  17. ),
  18. ),
  19. ),
  20. );
  21. `

これは検証に合格します。

  1. {
  2. "primary": {
  3. "name": "Primary",
  4. "color": "#ff6d69"
  5. },
  6. "secondary": {
  7. "name": "Secondary",
  8. "color": "#fecc50"
  9. }
  10. }

これは検証に失敗します。

  1. {
  2. "primary": {
  3. "name": "Primary",
  4. "color": "#ff6d69"
  5. },
  6. "secondary": "#fecc50"
  7. }

patternProperties

  1. たとえば、このスキーマは、各値が16進数カラーであり、プロパティが「単語」文字のみを含むことを要求します。
  2. ``````bash
  3. array(
  4. 'type' => 'object',
  5. 'patternProperties' => array(
  6. '^\\w+$' => array(
  7. 'type' => 'string',
  8. 'format' => 'hex-color',
  9. ),
  10. ),
  11. 'additionalProperties' => false,
  12. );
  13. `

これは検証に合格します。

  1. {
  2. "primary": "#ff6d69",
  3. "secondary": "#fecc50"
  4. }

これは検証に失敗します。

  1. {
  2. "primary": "blue",
  3. "$secondary": "#fecc50"
  4. }

REST APIがpatternPropertiesスキーマを検証する際、プロパティがパターンに一致しない場合、そのプロパティは許可され、内容に対して検証が適用されません。この論理が望ましくない場合は、スキーマにadditionalPropertiesを追加して、一致しないプロパティを許可しないようにします。

minPropertiesとmaxProperties

  1. ``````bash
  2. array(
  3. 'type' => 'object',
  4. 'additionalProperties' => array(
  5. 'type' => 'string',
  6. 'format' => 'hex-color',
  7. ),
  8. 'minProperties' => 1,
  9. 'maxProperties' => 3,
  10. );
  11. `

これは検証に合格します。

  1. {
  2. "primary": "#52accc",
  3. "secondary": "#096484"
  4. }

これは検証に失敗します。

  1. {
  2. "primary": "#52accc",
  3. "secondary": "#096484",
  4. "tertiary": "#07526c"
  5. }
  1. <a name="type-agnostic-keywords"></a>
  2. ### 型に依存しないキーワード
  3. #### oneOfとanyOf
  4. これらは、JSONスキーマバリデーターが値を検証する際に使用するスキーマの1つを選択できるようにする高度なキーワードです。`````anyOf`````キーワードは、値が指定されたスキーマのいずれかに一致することを許可します。一方、`````oneOf`````キーワードは、値が*正確に*1つのスキーマに一致することを要求します。
  5. たとえば、このスキーマは、エンドポイントに「操作」の配列を送信することを許可します。各操作は「クロップ」または「回転」である可能性があります。
  6. ``````bash
  7. array(
  8. 'type' => 'array',
  9. 'items' => array(
  10. 'oneOf' => array(
  11. array(
  12. 'title' => 'Crop',
  13. 'type' => 'object',
  14. 'properties' => array(
  15. 'operation' => array(
  16. 'type' => 'string',
  17. 'enum' => array(
  18. 'crop',
  19. ),
  20. ),
  21. 'x' => array(
  22. 'type' => 'integer',
  23. ),
  24. 'y' => array(
  25. 'type' => 'integer',
  26. ),
  27. ),
  28. ),
  29. array(
  30. 'title' => 'Rotation',
  31. 'type' => 'object',
  32. 'properties' => array(
  33. 'operation' => array(
  34. 'type' => 'string',
  35. 'enum' => array(
  36. 'rotate',
  37. ),
  38. ),
  39. 'degrees' => array(
  40. 'type' => 'integer',
  41. 'minimum' => 0,
  42. 'maximum' => 360,
  43. ),
  44. ),
  45. ),
  46. ),
  47. ),
  48. );
  49. `

REST APIは、oneOf配列に指定された各スキーマをループし、一致を探します。正確に1つのスキーマが一致する場合、検証は成功します。複数のスキーマが一致する場合、検証は失敗します。一致するスキーマがない場合、バリデーターは最も近い一致するスキーマを見つけて、適切なエラーメッセージを返します。

operations[0]は有効な回転ではありません。理由:operations[0][degrees]は0(含む)から360(含む)の間でなければなりません。
より役立つエラーメッセージを生成するために、各oneOfまたはanyOfスキーマにtitleプロパティを付与することを強く推奨します。

変更履歴

WordPress 5.6

  • multipleOf JSONスキーマキーワードをサポートします。r49063
  • minPropertiesおよびmaxProperties JSONスキーマキーワードをサポートします。r49053
  • patternProperties JSONスキーマキーワードをサポートします。r49082
  • anyOfおよびoneOf JSONスキーマキーワードをサポートします。r49246

WordPress 5.5

  • マルチタイプJSONスキーマサポートを改善しました。r48306
  • オブジェクトを検証する際に必須プロパティが提供されているかを確認します。r47809
  • formatキーワードをtypestringの場合にのみ検証します。r48300
  • uuid JSONスキーマフォーマットをサポートします。47753
  • hex-color JSONスキーマフォーマットをサポートします。r47450
  • pattern JSONスキーマキーワードをサポートします。r47810
  • minItemsmaxItems、およびuniqueItems JSONスキーマキーワードをサポートします。r47923 r48357
  • minLengthおよびmaxLength JSONスキーマキーワードをサポートします。r47627

WordPress 5.4

  • 空の文字列を空のオブジェクトに型ジャグリングすることをサポートします。r47362

WordPress 5.3

  • nullプリミティブ型をサポートし、基本的なマルチタイプ処理を実装します。r46249
  • スキーマに対してadditionalPropertiesを検証することをサポートします。r45807

WordPress 4.9

  • objectプリミティブ型をサポートします。r41727