WordPress REST APIとCORS

AMPプラグインのCORS 1
「がとらぼ」のAMPページはamp-listとWordPress標準のREST APIを使って「最近の投稿」リストを表示させるようにしていたのだが、いつのまにかGoogleのAMPキャッシュページではそのリストが表示されなくなっている。(上の画像の赤い破線の四角部分が空白になっている)
ローカルサーバのAMPページでは正しく表示されるので仕組みとしては正しい筈。また、CORSも解決済み(全許可)の筈なのに何故だ?。
(この半年ほどHTTPサーバのNginxの設定やテーマのfunctions.phpを弄り倒してるので自分でも何をどうしたかよく憶えていない。)

AMPプラグインのCORS 2
Chromeブラウザのデベロッパーツールでエラーを確認した。
HTMLヘッダのAccess-Control-Allow-Originの値を指摘されている。 https://gato-intaa-net.cdn.ampproject.orgと*の2つが指定されているのがダメよということ。
「*」 (全てのドメイン)は自分が指定したが、こんな複数指定に憶えがない。というかGoogleのAMPキャッシュのホストが何故勝手に指定されている?

「がとらぼ」のGoogle AMPキャッシュはホスト名(オリジン)が gato-intaa-net.cdn.ampproject.org なので、そのAMPキャッシュページを閲覧する状態を再現するためにそのオリジン指定を付けてREST APIで「がとらぼ」の「最近の投稿」のJSONを取得する。(下)
なお、CORSの確認ならオリジンは元ホスト以外なら正直何でも良い。

$ curl 'https://gato.intaa.net/wp-json/wp/v2/posts?__amp_source_origin=https%3A%2F%2Fgato.intaa.net' -I -H 'origin: https://gato-intaa-net.cdn.ampproject.org'
HTTP/2 200 
server: nginx
date: Tue, 12 Feb 2019 01:32:48 GMT
content-type: application/json; charset=UTF-8
amp-access-control-allow-source-origin: https://gato.intaa.net
x-robots-tag: noindex
x-content-type-options: nosniff
access-control-expose-headers: X-WP-Total, X-WP-TotalPages
access-control-allow-headers: Authorization, Content-Type
x-wp-total: 573
x-wp-totalpages: 58
link: <https://gato.intaa.net/wp-json/wp/v2/posts?page=2>; rel="next"
allow: GET
access-control-allow-origin: https://gato-intaa-net.cdn.ampproject.org
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-credentials: true
vary: Origin
strict-transport-security: max-age=31536000;
access-control-allow-origin: *
x-content-type-options: nosniff always
content-security-policy: default-src * 'self' data: 'unsafe-inline' 'unsafe-eval';

何故かaccess-control-allow-originヘッダが2つ出力されている。元々Nginxの設定で出力するようにしていたのが * の行でhttps://gato-intaa-net.cdn.ampproject.orgの行は知らない。
つまり、勝手にAccess-Control-Allow-Originヘッダが出力されている。
また、オリジンは何を指定してもその値がAccess-Control-Allow-Originヘッダに入るのでNginxの指定でいえば add_header Access-Control-Allow-Origin "$http_origin";と同じ状態らしい。

調べたところ、最近のWordPressで標準装備のREST APIを使うと勝手にHTTPレスポンスヘッダが3行ほど追加されるよう。

1
2
3
access-control-allow-origin: https://REST API呼び出し元ホスト(可変)
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-credentials: true

うーん、REST APIを使うときにCORSで困らないようにという親切なんだろうけど、勝手なことすんな!

ちなみにAMPプラグインのソースを見たところincludes/class-amp-http.phpにもHTTPレスポンスヘッダを付ける処理が追加されていた(v1.0から?)。こちらは何をするとそのヘッダが出力されるのかはまだ把握してないけど、いずれまた「勝手なことすんな!」と思う日が来るかも。

このAccess-Control-Allow-Originは勝手に追加出力(B)されると別に出力されている同ヘッダ(A)との組み合わせ(A且B)として判定されるので結果的に指定した値のどちらも許可されないという嫌らしいことになるみたい。

仕方がないのでNginxの設定で指定していた add_header access-control-allow-origin '*';の方を無効にしてみた。正直なところ「がとらぼ」はいろいろ掃除して外部からリソース共有(参照)されることもない筈なのでaccess-control-allow-originヘッダを付ける必要がなくなっていた。

いやいや、うちは、リソース提供するのでaccess-control-allow-originヘッダはHTTPサーバで常に出したいということであれば、WordPressのREST APIによって出力されるHTTPレスポンスヘッダの処理を上書きしてやるのが良さげ。

使用中のテーマのfunctions.phpの最後にでも追記
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//REST API HTTPレスポンスヘッダ制御上書き
add_action( 'rest_api_init', function() {
    remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
    add_filter( 'rest_pre_serve_request', function( $value ) {
        //header( 'Access-Control-Allow-Origin: ' . $origin );
        header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
        header( 'Access-Control-Allow-Credentials: true' );
        return $value;
    });
}, 15 );

5行目のコメントを外すと元と同じ動きになる筈。(そのときは上のコードは要らない筈だが。)

$ curl 'https://gato.intaa.net/wp-json/wp/v2/posts?__amp_source_origin=https%3A%2F%2Fgato.intaa.net' -I -H 'origin: https://gato-intaa-net.cdn.ampproject.org'
HTTP/2 200 
server: nginx
date: Tue, 12 Feb 2019 01:41:05 GMT
content-type: application/json; charset=UTF-8
amp-access-control-allow-source-origin: https://gato.intaa.net
x-robots-tag: noindex
x-content-type-options: nosniff
access-control-expose-headers: X-WP-Total, X-WP-TotalPages
access-control-allow-headers: Authorization, Content-Type
x-wp-total: 573
x-wp-totalpages: 58
link: <https://gato.intaa.net/wp-json/wp/v2/posts?page=2>; rel="next"
allow: GET
access-control-allow-origin: https://gato-intaa-net.cdn.ampproject.org
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-credentials: true
vary: Origin
strict-transport-security: max-age=31536000;
x-content-type-options: nosniff always
content-security-policy: default-src * 'self' data: 'unsafe-inline' 'unsafe-eval';

access-control-allow-originヘッダは1行だけになった。

AMPプラグインのCORS 3
「最新の投稿」のリスト自体はAMPキャッシュではない(そのためのamp-list + REST API化)ので結果はAMPページの再キャッシュ化を待つまでもなくすぐに確認できる。無事にリストが表示できるようになった。

対処は簡単だったけど、CORSは面倒で油断できない。

WordPressでAmazon広告 Amazon Associates Link Builder

インターネットショッピング
©いらすとや.

WordPressでアマゾンの商品広告を表示したいというときに日本ではAmazon JSが人気あるように思う。「がとらぼ」でも使ってきたし。
しかし、AMPを考慮するとJavascript+JQuery頼みのAmazon JSはちょっと困る。それとAmazon JSはWordPress本体のデータベースのテーブルの1つwp_optionsを汚染しすぎるし。
そこで乗り換え先のプラグインとしていろいろ探して見たが、Amazonアソシエイト推奨のAmazon Associates Link Builderで良くないかと思いはじめた。Javascriptを使わないテンプレートも使えるようだし。

Associates Link Builder 1
初期設定と使用するテンプレート切り替えで利用する設定画面。
初期設定の保存時だけでなく、テンプレート切り替え時など保存ボタンを押す直前に毎回チェックボックスにチェックする必要があるのが面倒っちゃ面倒。

Associates Link Builder 2
テンプレートの設定画面。
標準で用意されているテンプレートはPriceLink、ProductAd、ProductCarousel、ProductGrid、ProductLinkの5種類。標準(初期値)はProductCarouselだが、このテンプレートはjCarouselLiteを使ったスライド表示のためのJavascriptを含むので注意。
標準で存在する5つのテンプレートはそれを直接変更できないのでどこか変更して使いたいならベースのテンプレートを選んで[Clone]ボタンで複製し、名前を付けて自分用のテンプレートにする。複製したテンプレートと新規作成(Create new template)したテンプレートはいつでも編集可能(削除も)。
一般的な画像付き商品広告であればProductAdを複製したものをベースにするのが簡単かも。もちろん、1から作るのもありなので、その場合はCreate new templateを選択する。

Associates Link Builder 3
標準テンプレートのPriceLinkで表示したもの。オレンジ線の枠はテンプレートには含まれない。
その名のとおり価格だけ。

Associates Link Builder 4
標準テンプレートのProductAdで表示したもの。オレンジ線の枠はテンプレートには含まれない。シンプルだが商品画像も表示される。

Associates Link Builder 5
標準テンプレートのProductCarouselで表示したもの。オレンジ線の枠はテンプレートには含まれない。Productfrom Amazon.co.jpという表示のある部分がヘッダ。このテンプレートでは前述のとおりスライド表示で商品を切り替え表示できるみたい。最近はAMPを意識せざるを得ないのでJavascriptでのスライドとか興味が無く試していない。

Associates Link Builder 6
標準テンプレートのProductGridで表示したもの。オレンジ線の枠はテンプレートには含まれない。グリッドというだけあって、複数の商品を選択して登録すると上の画像のように複数の商品が並んで表示される。更に増やせば2x2など横だけでなく縦にも並ぶ。

Associates Link Builder 7
標準テンプレートのProductLinkで表示したが、WordPressの新しい標準テーマのTwenty Nineteenでは何も表示されなかった。Chromeブラウザのデベロッパツールで広告のある筈の部分が上の画像。肌色ともオレンジ色ともいえない塗りつぶし部分は広告が表示される筈のエリアのマージン部分みたい。実際の広告エリアはその塗りつぶし部分の中央を左右に1本見えるオレンジの線。エリアなので矩形であるべきところだが直線になっているということは上下に押し潰れている?

テンプレートの書き方

基本はHTMLとCSSなので最低限のことが解ってたら誰でも手を入れられる。Amazon Associates Link Builderではそこに専用の口髭タグ?変数?を埋めておくことでそこに記事中で指定したAmazon商品の情報が入って表示されるようになる。

  • {{Title}} アマゾンの商品のタイトルが入る。
  • {{DetailPageURL}} アマゾンの商品ページのURLが入る。
  • {{ASIN}} アマゾン商品番号(ASIN)が入る。
  • {{CurrentPrice}} 商品の現在価格が入る。通貨単位が付く。
  • {{CurrentPriceValue}} 商品の現在価格が入る。数字だけ。
  • {{StrikePrice}} 割引き前の価格(参考価格?)が入る。通貨単位が付く。
  • {{StrikePriceValue}} 割引き前の価格(参考価格?)が入る。数字だけ。
  • {{By}} 作者、アーティスト、メーカー名やブランドが入る。
  • {{Merchant}} 販売者(Amazonかマーケットプレイスの販売者)が入る。
  • {{InStock}} 在庫の有無が入る。
  • {{LargeImageURL}} 大きな商品画像が入る。
  • {{MediumImageURL}} 中サイズの商品画像が入る。
  • {{SmallImageURL}} 小さな商品画像が入る。
  • {{Prime}} プライムである場合にプライムであることを表示する。

上は基本的な商品情報タグだけ。詳しくはユーザーガイドを参照。
割引前価格や値下げ額など、その値が存在しない場合は基本的に空白になる。
また、タグによる簡素な条件分岐が可能で、商品が存在する場合だけ表示、在庫がある場合だけ表示などの表示分けもできる。なお、{{InStock}}はYes, True, No, Falseのような値が返ってくるだけのようなので在庫残り幾つとかは無理?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{{#Items}}
    {{#Item}}
        {{#aalb}} 

            商品を表示するためのHTMLタグと口髭タグを書く

            {{#SavingPercent}}
                割引がある場合だけ表示するHTMLタグと口髭タグを書く
            {{/SavingPercent}}

            商品を表示するためのHTMLタグと口髭タグを書く

            {{#StrikePrice}}
                割引前価格(参考価格)がある場合だけ表示するHTMLタグと口髭タグを書く
            {{/StrikePrice}}

            商品を表示するためのHTMLタグと口髭タグを書く

            {{#Prime}}
                プライム商品の場合だけ表示するHTMLタグと口髭タグを書く
            {{/Prime}}

            商品を表示するためのHTMLタグと口髭タグを書く

        {{/aalb}}
    {{/Item}}
{{/Items}}

{{#Items}}{{#Item}}{{#aalb}} ・・・ {{/aalb}}{{/Item}}{{/Items}}の部分はほぼ無条件で書けば良いかと思われる。
購入できない商品の広告を表示するのは無駄なので例えば在庫切れなら表示させないということであれば(下)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{#Items}}
    {{#Item}}
        {{#aalb}} 
             {{#InStock}}

                 在庫がある場合だけ表示するHTMLタグと口髭タグを書く

             {{/InStock}}
        {{/aalb}}
    {{/Item}}
{{/Items}}

CSSについては、AMPプラグインのClassicモードでは読み込まれないためAMPページではデザインが崩れることになる。CSSの内容を(丸ごとコピーして)AMPプラグインのstyle.phpに追記することで問題は回避されるが、AMPページではCSSの!importantは使ってはいけないのでstyle.phpに混入させないよう注意。なお、AMPプラグインディレクトリ下のtemplates内にあるstyle.phpを直接編集してはいけない。(プラグイン更新時に変更が失われるので)。使用中テーマのディレクトリ内にampというディレクトリを作成してその下にwp-content/plugins/amp/templates/style.phpからコピーしてそのファイルを編集する。

記事への広告埋め込み

2019年2月10日現在のAssociates Link BuilderはWordPress5.0から標準となったグーテンベルクエディタに対応していない。また、旧ビジュアルエディタ(リッチエディタ)が必須となるので使用しているテーマのfunctions.phpの最後辺りに1行追加。
add_filter('use_block_editor_for_post', '__return_false');

また、使用しているテーマのfunctions.php内に既に次の行が存在するなら削除するかコメント化して無効にする。(コメント化は無効にしたい行の先頭に // を付ける)
add_filter('user_can_richedit', '__return_false');

↑はリッチエディタを無効にするフィルタ。これはWordPressのユーザーの「プロフィール」画面で「ビジュアルリッチエディタを使用しない」のチェックを付けるのと同じ効果。(チェックするのと効果が同じだが、それによってチェックが付くわけではないので注意。チェックしてないのにリッチエディタが使えないと悩むことになることもある。)

Associates Link Builder 8
直前に書いたようにAssociates Link Builderを使うならリッチエディタを有効化しないと記事への商品埋め込みが機能しないので、「ビジュアルエディタを使用しない」にチェックしている場合は外す。

Associates Link Builder 9
リッチエディタを有効化しなくてはAmazon商品検索が機能しないが、ビジュアルモードを使わなくてはならないということではない。テキストモード(コード入力)でも正常に機能する。
広告を入力したいところにカーソルを合わせてからテキストボックス上部にあるAmazon検索窓にキーワードやASINを入力して[Search]ボタンを押す。

Associates Link Builder 10
検索結果が表示されるので広告として表示したい商品があればクリックする。グリッドやスライドなど複数の商品を表示できるテンプレートでは複数商品をクリックできる。クリックした商品は下側のボックスに表示される。
[Add Shortcode]ボタンを押せば記事中に広告用ショートコードが記入される。

Amazon Associates Link Builderプラグインを使用するとWordPress用データベースにはwp_aalb_asin_responseというテーブルが作成されそこにキャッシュ的な商品情報データが蓄積される。商品情報1件あたりのデータが意外と大きい。商品に関する情報でAmazonから入手できるもの全てを取り込んでるみたい。このデータが使われると広告の表示はアマゾンへの問い合わせ無しに高速に行えるという利点はあるかもしれないが、広告の商品の価格が変わったとか在庫の有無などの情報が変わったときのデータ更新はどうなるのかしら?特に広告商品が増えた場合。AmazonJSはキャッシュの削除をボタン1つでできるのでその点は負けてるかも。

Up