kintone のレコード詳細ページから Amazon 商品データを参照するテクニック

著者名:長井 祥和( 合同会社アクアビット (External link)

目次

はじめに

kintone アプリで資産を管理したいという要望をよく聞きます。
その際、既存サイトのデータが参照できたら便利ですよね。
たとえば商品データを参照したい場合、Amazon API を通して取得したいといった要件は十分考えられます。
本稿では、kintone の詳細レコード上に、Amazon API から取得したデータを表示する方法について紹介します。

必要な環境

本件を実現するために必要な環境は次のとおりです。

パソコン

ノートかデスクトップかの違いは問いません。
モバイルや俗にいうガラパゴス携帯、またタブレットからの利用は考慮していません。

Web ブラウザー

本件では Google Chrome、Firefox、Internet Explorer 9 と 11 で検証しています。
その他のブラウザーでは予測不可能な動作に遭遇するかもしれません。

PHP が動作するサーバー

今回の検証で使用したレンタルサーバーは、ini も変更できず、pear などのライブラリも利用できません。
SSL も専用ではく共用 SSL を使用しています。

PHP ネイティブだけを頼りに構築しているので、サーバー間の動作差異はあまりないと思えます。
なお、レンタルサーバーの OS は UNIX 系で、Apache 2.2 で動いているものを使いました。
PHP のバージョンは 5.3.2 です。

kintone アプリ

最低限、次の 3 つのフィールドが必要です。

フィールドタイプ フィールド名 フィールドコード 説明
文字列(1 行) ISBNコード F_Isbn ISBNコード用
スペース Space_Image Amazon情報表示用スペース
スペース Large_Image 画像表示用スペース

これらのフィールドを用意して、kintone カスタマイズを適用すると、レコード詳細画面に Amazon から取得した商品データが表示されます。

なお、kintone アプリには上記以外のフィールドも追加して表示しています。

Amazon の認証情報

今回作成するサンプルに必要な次の 3 つの情報を準備します。

  • Access Key ID
  • Secret Access Key
  • トラッキング ID

これらの認証情報を確認する方法は、今回割愛します。

PHP ファイル

PHP を利用した Amazon へのパラメーターの生成方法

kntone から Amazon の情報を取得するのに php を介さなければならないかについて、一言申し添えておかねばなりません。
この Tips をご覧の皆様は、すでに有識者の皆様が執筆された秀逸な Tips の数々にも目を通されたことでしょう。
次の Tips では、kintone から外部 API に接続する内容です。

私も本稿を書くにあたって参考にしました。

これらの Tips は kintone の JavaScript API を使用しています。
JavaScript 内に外部 API の URL を直接書き込み、その結果を表示させる方法です。
一方、Amazon API を呼び出す際の URL ですが、その中には先ほど確認した Amazon の認証情報から生成した署名文字列を含める必要があります。
つまり、署名文字列を生成するにあたっては、JavaScript 内に次の機密情報を埋め込まねばなりません。

  • Access Key ID
  • Secret Access Key
  • トラッキング ID

この回のカスタマイズでは、それを避けるために、外部の PHP に署名文字列の生成や機密情報の扱いを任せようというのが狙いです。

kintone に埋め込む JavaScript 内で呼び出す API の URL は次のとおりです。

1
https://example.com/AmazonBookInfo.php?code=ISBN&no=<ISBN>

<ISBN> は、kintone のフィールドに入力されている ISBN コードです。
Amazon 認証情報に関する情報の一切は JavaScript からは省かれています。

AmazonBookInfo.php

kintone からのリクエストは、次の PHP ファイル(AmazonBookInfo.php)で処理されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
    header("Cache-Control: no-cache, must-revalidate");
    header('Access-control-allow-origin:*');
    header('Access-Control-Allow-Headers:*');
    header('Content-type: application/xml; charset=UTF-8;');
    header('p3p: CP=COR');

    require_once('SetBookByAmazon.php');

    $ISBN     = $_GET["no"];
    $BookCode = $_GET["code"];

    $return = SetBookByAmazon($ISBN, $BookCode);
    echo $return;
?>

JavaScript に値を返すための各種ヘッダーの記述と、Amazon から情報を取得する本体の PHP の「SetBookByAmazon.php」の呼び出しが書かれています。
ヘッダーについては Google Chrome と Firefox、そして Internet Explorer 9 で正常にデータが渡せることを確認できています。
これは環境によって違いますので、工夫してみてください。
特に 4 番目のヘッダーで application/xml となっています。これは JavaScript に対して渡すデータの種類を XML として規定する部分ですから、重要です。

SetBookByAmazon.php

次に「AmazonBookInfo.php」から呼び出しされた「SetBookByAmazon.php」のコードを示します。

 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
 * Amazon ISBN Data
 * Copyright (c) 2015 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

<?php
function urlencode_rfc3986($str){
    return str_replace('%7E', '~', rawurlencode($str));
}
function SetBookByAmazon($BookId,$BookCode ){
    define('AccessKeyId','ICHIBANSAISHINNOAKUSESUKII');
    define('SecretAccessKey','DAIJINADAIJINASIIKURETTOAKUSESUKIIDESUNENN');
    define('AssociateTag','anatanoiddesu-99');

    $baseurl = 'http://ecs.amazonaws.jp/onca/xml';
    $params = array();
    $params['AWSAccessKeyId'] = AccessKeyId;
    $params['AssociateTag']   = AssociateTag;
    $params['IdType']         = $BookCode;
    $params['ItemId']         = $BookId;
    $params['Operation']      = 'ItemLookup';
    $params['ResponseGroup']  = 'ItemAttributes,Images';
    $params['SearchIndex']    = 'Books';
    $params['Service']        = 'AWSECommerceService';
    $params['Timestamp']      = gmdate('Y-m-dTH:i:sZ');
    $params['Version']        = '2009-03-31';

    ksort($params);

    $canonical_string = '';
    foreach ($params as $k => $v) {
        $canonical_string .= '&'.urlencode_rfc3986($k).'='.urlencode_rfc3986($v);
    }
    $canonical_string = substr($canonical_string, 1);

    $parsed_url = parse_url($baseurl);
    $string_to_sign = "GETn{$parsed_url['host']}n{$parsed_url['path']}n{$canonical_string}";
    $signature = base64_encode(hash_hmac('sha256', $string_to_sign, SecretAccessKey, true));

    $url = $baseurl.'?'.$canonical_string.'&Signature='.urlencode_rfc3986($signature);

    //echo $url;

    $response = file_get_contents($url);
    echo $response;
}

次の行の 3 つのパラメーターの値は、それぞれ Amazon の認証情報 で確認した値に置き換えてください。

12
13
14
    define('AccessKeyId','ICHIBANSAISHINNOAKUSESUKII');
    define('SecretAccessKey','DAIJINADAIJINASIIKURETTOAKUSESUKIIDESUNENN');
    define('AssociateTag','anatanoiddesu-99');

動作確認

「AmazonBookInfo.php」と「SetBookByAmazon.php」をサーバーにアップロードしたら、Web ブラウザーで「AmazonBookInfo.php」のパスにアクセスしてください。
XML 形式で本の情報が表示されたことを確認します。

1
https://example.com/AmazonBookInfo.php?code=ISBN&no=<ISBN>

今回は ISBN のみに焦点を当てて書籍情報を取得しています。
「SetBookByAmazon.php」に記述している Amazon API の URL に渡すパラメーター次第で、さまざまな商品情報を取得できます。
今回使用した API の詳細は、次のページを参照してください。
Product Advertising API | ItemLookUp (External link)

プログラムの解説

商品の情報
22
23
    $params['IdType']         = $BookCode;
    $params['ItemId']         = $BookId;

kintone カスタマイズファイルから渡された変数が格納される場所です。

Amazonから取得する情報の種類
25
    $params['ResponseGroup']  = 'ItemAttributes,Images';

Amazon から取得する情報の種類を指定します。
それぞれ以下を示しています。

  • ItemAttributes:商品詳細
  • Images:画像情報

複数の種類を指定する場合はカンマ(,)でつなげます。

署名作成の準備
31
ksort($params);

指定した各種パラメーターをそのパラメーター名称で並び替えます。
署名作成時に必要な処理です。

各種パラメーターのエンコード
33
34
35
36
37
    $canonical_string = '';
    foreach ($params as $k => $v) {
        $canonical_string .= '&'.urlencode_rfc3986($k).'='.urlencode_rfc3986($v);
    }
    $canonical_string = substr($canonical_string, 1);

指定した各種パラメーターをエンコードします。
RFC3986 (External link) で定められた方法に準じています。

署名文字列の生成
39
40
41
    $parsed_url = parse_url($baseurl);
    $string_to_sign = "GETn{$parsed_url['host']}n{$parsed_url['path']}n{$canonical_string}";
    $signature = base64_encode(hash_hmac('sha256', $string_to_sign, SecretAccessKey, true));

ハッシュ関数を使用して、Secret Access Key を元の文字列が推測できない文字列に変換します。

商品情報の取得(Amazon API の実行)
47
$response   = file_get_contents($url);

組み立てた Amazon API の URL を実行します。
レスポンスボディは XML 形式で返却されます。

リクエスト結果の表示と返却
48
echo $response;

API から返却された文字列を表示します。
この表示処理によって、呼び出し元へ文字列が戻されます。

注意事項

セキュリティの観点から、2 つのファイルをアップロードしたサーバーは HTTPS 化してください。
kintone セキュアコーディングガイドライン | 通信に HTTPS を使用する

また kintone の URL は https://sample.cybozu.com/ のように https で始まるため、PHP サーバーが HTTP 化していないとブラウザーによって好ましからざる動きをすることもあります。

kintone カスタマイズ

PHP サーバー側で、 SBN から Amazon の書籍情報を取得する処理が完成しました。
あとは kintone カスタマイズで受け取って加工するだけです。

kintone アプリにカスタマイズを適用する方法はこの記事では割愛します。
詳しくは カスタマイズファイルの適用方法 を確認してください。

Amazon API を実行するタイミング

このカスタマイズではレコードの詳細画面で Amazon 情報を取得します。
そのため、レコード編集画面を開いたときの実装は割愛しています。

場合によっては、詳細画面から Amazon API を呼び出したときに「Http/1.1 Service Unavailable」エラーが発生します。
レコード詳細画面を開くという操作は頻繁に行う操作のため、Amazon API へのリクエスト回数が制限値を超過してしまうことが原因のようです。
このエラー対策については、jQuery の Ajax で、Error ハンドラーから再帰呼び出しを試しましたが、callee と use strict が両立できませんでした。
なので、時間をおいて再実行ということでご了承ください。

クロスドメイン制約

PHP サーバーへのリクエストを送信するとき、「sample.cybozu.com」から「example.com」といった異なるドメインのサーバーに Ajax リクエストを発行します。
しかし別ドメインへのリクエストはセキュリティリスクにつながるため、Web ブラウザーでは簡単に通信できないようになっています。
これをクロスドメイン制約といいます。

このカスタマイズでは、kintone と PHP サーバーとのやりとりに XML 形式でやりとりしています。
JSONP を使えばクロスドメイン制約についても解決できるようです。

ソースコード

このカスタマイズには jQuery を使用します。
カスタマイズを適用する際には、サイボウズが提供する Cybozu CDN の jQuery の URL を指定してください。 また、次の内容をテキストエディタに貼り付け、「AmazonCallBookInfoDetail.js」という名前で保存します。

  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
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
 * Amazon ISBN Data
 * Copyright (c) 2015 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

(function() {
  'use strict';

  jQuery.support.cors = true;
  kintone.events.on('app.record.detail.show', (event) => {
    const userAgent = window.navigator.userAgent.toLowerCase();
    let Isbn = '';
    Isbn = event.record.F_Isbn.value;
    Isbn = Isbn.replace(/-/g, '');
    Isbn = Isbn.replace(/ISBN/g, '');
    const Space_Amazon = kintone.app.record.getSpaceElement('Space_Image');
    const Space_LargeImg = kintone.app.record.getSpaceElement('Large_Image');
    let errormsg;

    if ((Isbn.substr(Isbn.length - 1, 1)).toLocaleUpperCase() !== 'X' && isNaN(Isbn, 10)) {
      Space_Amazon.innerHTML = 'Isbnの値が不正な文字列または記号を含んでいます。';
      return event;
    }
    Space_LargeImg.style.width = 311;
    Space_Amazon.innerHTML = 'データを読み込んでいます。';

    $.ajax({
      type: 'POST',
      url: 'https://example.com/AmazonBookInfo.php?code=ISBN&no=' + Isbn + '&now=' + parseInt((new Date()) / 1000, 10),
      dataType: 'xml',
      cache: false,
      async: true,
      timeout: 10000,
      success: (data) => {
        let aws_ASIN; // Amazon商品コード
        let aws_DetailPageURL; // Amazon詳細ページ
        let aws_AddToWishlist; // AmazonWishリストページ
        let aws_TellAFriend; // Amazon友だちに教えるページ
        let aws_AllCustomerReviews; // Amazonカスタマーレビューページ
        let aws_AllOffers; // Amazon全ての提案ページ
        let aws_SwatchImageURL; // 見本用イメージ画像URL
        let aws_SwatchImageHeight; // 見本用イメージ縦幅
        let aws_SwatchImageWidth; // 見本用イメージ横幅
        let aws_SmallImageURL; // 小イメージ画像URL
        let aws_SmallImageHeight; // 小イメージ縦幅
        let aws_SmallImageWidth; // 小イメージ横幅
        let aws_ThumbnailImageURL; // サムネイル用イメージ画像URL
        let aws_ThumbnailImageHeight; // サムネイル用イメージ縦幅
        let aws_ThumbnailImageWidth; // サムネイル用イメージ横幅
        let aws_TinyImageURL; // 極小イメージ画像URL
        let aws_TinyImageHeight; // 極小イメージ縦幅
        let aws_TinyImageWidth; // 極小イメージ横幅
        let aws_MediumImageURL; // 中イメージ画像URL
        let aws_MediumImageHeight; // 中イメージ縦幅
        let aws_MediumImageWidth; // 中イメージ横幅
        let aws_LargeImageURL; // 大イメージ画像URL
        let aws_LargeImageHeight; // 大イメージ縦幅
        let aws_LargeImageWidth; // 大イメージ横幅
        let aws_Author; // 著者
        let aws_Creator; // 著者(編者等)
        let aws_Binding; // 装丁
        let aws_EAN; // 商品識別コード
        let aws_EANList; // 商品識別コードリスト
        let aws_IsAdultProduct; // アダルト商品フラグ
        let aws_ISBN; // ISBNコード
        let aws_Label; // レーベル
        let aws_LanguageName; // 使用言語名
        let aws_LanguageType; // 使用言語タイプ
        let aws_ListPriceAmount; // 価格
        let aws_ListPriceCurrencyCode; // 価格通貨コード
        let aws_ListPriceFormattedPrice; // 表示価格
        let aws_Manufacturer; // 生産者
        let aws_NumberOfPages; // ページ数
        let aws_PackageDimensionsHeight; // 商品サイズ縦
        let aws_PackageDimensionsLength; // 商品サイズ奥行
        let aws_PackageDimensionsWeight; // 商品重量
        let aws_PackageDimensionsWidth; // 商品サイズ横
        let aws_ProductGroup; // 商品種別
        let aws_ProductTypeName; // 商品種別名称
        let aws_PublicationDate; // 発行日
        let aws_Publisher; // 発行者
        let aws_ReleaseDate; // 発売日
        let aws_SKU; // 商品管理コード
        let aws_Studio; // スタジオ
        let aws_Title; // 題名

        $(data).find('Item').each(function() {
          if ($(this).find('ItemAttributes').find('ISBN').text()) {
            aws_ASIN = $(this).find('ASIN').text();
            aws_DetailPageURL = $(this).find('DetailPageURL').text();
            $(this).find('ItemLink').find('Description').each(function() {
              if ($(this).text() === 'Add To Wishlist') {
                aws_AddToWishlist = $(this).next().text();
              }
              if ($(this).text() === 'Tell A Friend') {
                aws_TellAFriend = $(this).next().text();
              }
              if ($(this).text() === 'All Customer Reviews') {
                aws_AllCustomerReviews = $(this).next().text();
              }
              if ($(this).text() === 'All Offers') {
                aws_AllOffers = $(this).next().text();
              }
            });
            $(this).find('ImageSet').each(function() {
              aws_SwatchImageURL = $(this).find('SwatchImage').find('URL').text();
              aws_SwatchImageHeight = $(this).find('SwatchImage').find('Height').text();
              aws_SwatchImageWidth = $(this).find('SwatchImage').find('Width').text();
              aws_SmallImageURL = $(this).find('SmallImage').find('URL').text();
              aws_SmallImageHeight = $(this).find('SmallImage').find('Height').text();
              aws_SmallImageWidth = $(this).find('SmallImage').find('Width').text();
              aws_ThumbnailImageURL = $(this).find('ThumbnailImage').find('URL').text();
              aws_ThumbnailImageHeight = $(this).find('ThumbnailImage').find('Height').text();
              aws_ThumbnailImageWidth = $(this).find('ThumbnailImage').find('Width').text();
              aws_TinyImageURL = $(this).find('TinyImage').find('URL').text();
              aws_TinyImageHeight = $(this).find('TinyImage').find('Height').text();
              aws_TinyImageWidth = $(this).find('TinyImage').find('Width').text();
              aws_MediumImageURL = $(this).find('MediumImage').find('URL').text();
              aws_MediumImageHeight = $(this).find('MediumImage').find('Height').text();
              aws_MediumImageWidth = $(this).find('MediumImage').find('Width').text();
              aws_LargeImageURL = $(this).find('LargeImage').find('URL').text();
              aws_LargeImageHeight = $(this).find('LargeImage').find('Height').text();
              aws_LargeImageWidth = $(this).find('LargeImage').find('Width').text();
            });
            aws_Author = $(this).find('ItemAttributes').find('Author').text();
            aws_Creator = $(this).find('ItemAttributes').find('Creator').text() + ' ' + $(this).find('ItemAttributes').find('Creator').attr('Role');
            aws_Binding = $(this).find('ItemAttributes').find('Binding').text();
            aws_EAN = $(this).find('ItemAttributes').find('EAN').text();
            aws_EANList = $(this).find('ItemAttributes').find('EANList').find('EANListElement').text();
            aws_IsAdultProduct = $(this).find('ItemAttributes').find('IsAdultProduct').text();
            aws_ISBN = $(this).find('ItemAttributes').find('ISBN').text();
            aws_Label = $(this).find('ItemAttributes').find('Label').text();
            aws_LanguageName = $(this).find('ItemAttributes').find('Languages').find('Language').find('Name').text();
            aws_LanguageType = $(this).find('ItemAttributes').find('Languages').find('Language').find('Type').text();
            aws_ListPriceAmount = $(this).find('ItemAttributes').find('ListPrice').find('Amount').text();
            aws_ListPriceCurrencyCode = $(this).find('ItemAttributes').find('ListPrice').find('CurrencyCode').text();
            aws_ListPriceFormattedPrice = $(this).find('ItemAttributes').find('ListPrice').find('FormattedPrice').text();
            aws_Manufacturer = $(this).find('ItemAttributes').find('Manufacturer').text();
            aws_NumberOfPages = $(this).find('ItemAttributes').find('NumberOfPages').text();
            aws_PackageDimensionsHeight = $(this).find('ItemAttributes').find('PackageDimensions').find('Height').text();
            aws_PackageDimensionsLength = $(this).find('ItemAttributes').find('PackageDimensions').find('Length').text();
            aws_PackageDimensionsWeight = $(this).find('ItemAttributes').find('PackageDimensions').find('Weight').text();
            aws_PackageDimensionsWidth = $(this).find('ItemAttributes').find('PackageDimensions').find('Width').text();
            aws_ProductGroup = $(this).find('ItemAttributes').find('ProductGroup').text();
            aws_ProductTypeName = $(this).find('ItemAttributes').find('ProductTypeName').text();
            aws_PublicationDate = $(this).find('ItemAttributes').find('PublicationDate').text();
            aws_Publisher = $(this).find('ItemAttributes').find('Publisher').text();
            aws_ReleaseDate = $(this).find('ItemAttributes').find('ReleaseDate').text();
            aws_SKU = $(this).find('ItemAttributes').find('SKU').text();
            aws_Studio = $(this).find('ItemAttributes').find('Studio').text();
            aws_Title = $(this).find('ItemAttributes').find('Title').text();
          }
        });
        if (aws_ASIN) {
          Space_Amazon.innerHTML =
                            '題名:' + aws_Title + '<br />' +
                            '著者:' + (aws_Author ? aws_Author : aws_Creator) + '<br />' +
                            'EAN:' + aws_EAN + '<br />' +
                            'EANlist:' + aws_EANList + '<br />' +
                            'ISBN:' + aws_ISBN + '<br />' +
                            '単一商品コード:' + aws_SKU + '<br />' +
                            'Amazon商品コード:' + aws_ASIN + '<br />' +
                            '装丁:' + aws_Binding + '<br />' +
                            '価格:' + aws_ListPriceFormattedPrice + '<br />' +
                            'ページ数:' + aws_NumberOfPages + '<br />' +
                            '出版日:' + aws_PublicationDate + ' 発売日:' + aws_ReleaseDate + '<br />' +
                            '出版社:' + aws_Publisher + '<br />' +
                            'レーベル:' + aws_Label + '<br />' +
                            '製作者:' + aws_Manufacturer + '<br />' +
                            'スタジオ:' + aws_Studio + '<br />' +
                            '言語:' + aws_LanguageName + '<br />' +
                            '縦:' + aws_PackageDimensionsHeight + ' 奥:' + aws_PackageDimensionsLength + ' 横:' + aws_PackageDimensionsWidth + ' 重量:' + aws_PackageDimensionsWeight + '<br />' +
                            '商品種類:' + aws_ProductGroup + ' 商品分類:' + aws_ProductTypeName + '<br />' +
                            '<a href=\'' + aws_DetailPageURL + '\' target=\'_blank\'>Amazon詳細ページ</a><br />' +
                            '<a href=\'' + aws_AddToWishlist + '\' target=\'_blank\'>AmazonWishリストページ</a><br />' +
                            '<a href=\'' + aws_TellAFriend + '\' target=\'_blank\'>Amazon友だちに教えるページ</a><br />' +
                            '<a href=\'' + aws_AllCustomerReviews + '\' target=\'_blank\'>Amazonカスタマーレビューページ</a><br />' +
                            '<a href=\'' + aws_AllOffers + '\' target=\'_blank\'>Amazon全ての提案ページ</a><br />';
          if (userAgent.indexOf('firefox') !== -1) {
            Space_LargeImg.innerHTML = '<img src=\'' + aws_LargeImageURL.replace('http:', '') + '\' width=\'' + aws_LargeImageWidth + '\' height=\'' + aws_LargeImageHeight + '\' />';
          } else {
            Space_LargeImg.innerHTML = '<img src=\'' + aws_LargeImageURL + '\' width=\'' + aws_LargeImageWidth + '\' height=\'' + aws_LargeImageHeight + '\' />';
          }
        } else {
          Space_Amazon.innerHTML = 'エラー名称:' +
                            $(data).find('Items').find('Request').find('Errors').find('Error').find('Code').text() +
                            '<br />' +
                            'エラー内容:' +
                            $(data).find('Items').find('Request').find('Errors').find('Error').find('Message').text();
        }
      },
      error: function(XMLHttpRequest, textStatus, errorThrown) {
        errormsg = '--- エラー情報 ---';
        errormsg = errormsg + '<BR>エラー番号:' + XMLHttpRequest.status;
        errormsg = errormsg + '<BR>エラー詳細:' + XMLHttpRequest.statusText;
        errormsg = errormsg + '<BR>エラー内容:' + textStatus;
        errormsg = errormsg + '<BR>エラー発生箇所:' + errorThrown;
        for (let i = 0; i < errorThrown; i++) {
          errormsg = errormsg + '<BR>error ' + i + ':' + errorThrown[i];
        }
        window.alert(errormsg);
      }
    });

    return event;
  });

})();

プログラムの解説

ブラウザー種別の判別
14
    const userAgent = window.navigator.userAgent.toLowerCase();

実行しているブラウザーの種別をここで取得しています。
呼び出し先の URL が https から始まっても、画像は http が返されることがあり、それを防ぐために一部ブラウザーによって画像のアドレスを変更しています。

ISBN のフォーマットチェック
23
24
25
26
    if ((Isbn.substr(Isbn.length - 1, 1)).toLocaleUpperCase() !== 'X' && isNaN(Isbn, 10)) {
      Space_Amazon.innerHTML = 'Isbnの値が不正な文字列または記号を含んでいます。';
      return event;
    }

本稿で実装する内容では、ユーザ入力データからのサニタイズ処理はあまりありません。
唯一この場所で実施しています。
数値かをチェックしていますが、ISBN の末尾一桁のチェックディジット結果が X となる場合のみ許可しています。

Amazon API の URL の生成
32
      url: 'https://example.com/AmazonBookInfo.php?code=ISBN&no=' + Isbn + '&now=' + parseInt((new Date()) / 1000, 10),

先に紹介しましたが、codeIsbn という 2 つのパラメーター以外に、now というパラメーターも追加しています。
実は now パラメーターは以下の処理では使われていません。
投げるリクエスト URL を常に変化させないと Internet Explorer 上でうまく値が戻ってこないという情報から、このような記載を設けています。

キャッシュの無効化
34
      cache: false,

キャッシュを OFF にしないと Internet Explorer で値を取得できないことがあるため追記しています。

また、Amazon からの XML データを扱うときに気を付けるべき点があります。
それは書籍と Kindle 書籍の場合です。
Kindle 書籍は ISBN 項目を持っていないのですが、Amazon の仕様で ISBN に対してデータ検索結果に Kindle データも含まれます。
XML 上では、2 つの <Item> タグが紐付いています。
その場合は、JavaScript 側で <Item> タグ内をループさせるのですが、<ISBN> タグの有無で判別しています。

だいたいの XML データの構造がおわかりいただけましたでしょうか。
項目については ItemAttributes レスポンスグループ (External link) で説明されています。

Amazon の商品情報の HTML の生成
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
        $(data).find('Item').each(function() {
          if ($(this).find('ItemAttributes').find('ISBN').text()) {
            // ...
            $(this).find('ImageSet').each(function() {
              // ...
            });
          }
        }
        if (aws_ASIN) {
          Space_Amazon.innerHTML = // ...
        });

XML のデータが格納された変数 data に対し、<Item> タグの要素を検索し、さらにその中で <ImageSet> タグを検索しています。
そして必要な情報を取得した後に innerHTML を使用して書き込むべき文字列を生成しています。

kintone への適用

jQuery CDN の URL と「AmazonCallBookInfoDetail.js」を kintone アプリに適用します。

スクリーンショットでは「jQuery.xdomainrequest.min.js」が適用されていますが、現在は適用不要です。
このファイルは、Internet Explorer 9 以前のブラウザーでクロスドメイン制約に対処するため必要なライブラリです。
MoonScript/jQuery-ajaxTransport-XDomainRequest (External link)

全体の動作確認

ISBN を入力したレコードを開いて、次のように表示できることを確認してください。

レコードを移動すると新たなレコード先で Amazon から呼び出されたデータが表示されます。
ただし、あまり短い時間で呼び出すと Amazon からの応答が途絶えてしまいます。
その場合は F5 でリロードしてください。

まとめ

外部認証が必要な複雑な API を呼び出す場合、外部サーバーで API を実行する方法がわかると、さまざまなケースで kintone をより一層活用できます。
ただしくれぐれも過度な Amazon へのリクエストは控えてください。
1 時間当たりのリクエスト回数に上限があるためです。