jQuery と JSONP で Bing の画像検索を利用してみる

Bing のレスポンスで「invalid label」というエラーが発生した原因

Bing のレスポンスで「invalid label」というエラーが発生した原因は、Bing で JSONP として処理をするには、「JsonType」と「JsonCallback」をリクエスト時に設定していなかったことが原因。これをリクエスト時に設定すれば、「invalid label」のエラーは発生しない。
「JsonType」には JavaScript で処理するために、「callback」値を設定する。「JsonCallback」には callback の関数名を設定する。
id:sshi さんにコメントで、JSON では数値を「"」で囲まなくていい仕様と指摘されていた。自分の勘違いに気がついたのは午前 2時過ぎだったので、今になってやっと訂正…。
console.log() で値を確認するために、FireFox + FireBug が必要。

サンプルコード

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="js/jquery-1.4.1.min.js"></script>
<script type="text/javascript">
$(function() {
    var query = 'ソメイヨシノ';
    searchImagesWithBing(query);
});

function searchImagesWithBing(query) {
    // JSON で検索結果を取得するための問い合わせ先 URL
    var url      = 'http://api.search.live.net/json.aspx';
    var appid    = 'Enter Your App ID';
    var source   = 'image';
    var count    = '10';
    var offset   = '0';
    var version  = '2.2';
    // 検索結果を JSON 形式で取得し JSONP として処理するための設定値
    var jsontype = 'callback';

    $.ajax({
        dataType: 'jsonp',
        data: {
            Appid:          appid,
            Query:          query,
            Sources:        source,
            'Image.Count':  count,
            'Image.Offset': offset,
            Version:        version,
            JsonType:       jsontype
        },
        cache: true,
        // Bing API で指定する callback のパラメーター「名」
        // パラメーター「値」ではないことに注意
        jsonp: 'JsonCallback',
        //url: 'http://api.search.live.net/json.aspx?&JsonCallback=?&',
        url: url,
        error: function(response) {
            console.log(response);
            console.log('Response Error.');
        },
        success: function (response) {
            var errors = response.SearchResponse.Errors;
            if (errors != null) {
                console.log('Request Error.');
                console.log(errors);
            } else {
                var imgElement   = null;
                var aElement     = null;
                var title        = null;
                var img_medium   = null;
                var html_medium  = null;
                var img_small    = null;
                var width_small  = 0;
                var height_small = 0;
                var results = response.SearchResponse.Image.Results;
                for (var i = 0; i < results.length; i ++) {
                    title        = results[i]['Title'];
                    img_medium   = results[i]['MediaUrl'];
                    html_medium  = results[i]['Url'];
                    img_small    = results[i]['Thumbnail']['Url'];
                    width_small  = results[i]['Thumbnail']['Width'];
                    height_small = results[i]['Thumbnail']['Height'];
                    imgElement = '<img src="' + img_small + '" width="' + width_small + '" height="' + height_small + '" alt="' + title + '" />';
                    aElement   = '<a href="' + img_medium + '">' + imgElement + '</a>';
                    $('<div class="image"></div>').append(aElement).appendTo('body');
                }
            }
        }
    });
}
</script>
<title>Bing Image</title>
</head>
<body>
</body>
</html>

jQuery と JSONP と $.ajax() と無名関数

JavaScript のコードをほとんど書いたことがなく、jQueryJSONP も初めてだったので、戸惑うことが多かった。

Bing で callback で利用する関数を指定するパラメーター名は JsonCallback

Bing のように callback 関数を明示的に指定する必要がある場合は、

$.ajax({
    url: 'http://localhost/ajax',
    dataType: 'jsonp',
    query: 'hoge',
    type: 'fuga',
    data: {
        ...
    },
    jsonp: 'JsonCallback', // jsonp: に callback 関数を指定するパラメータ名を設定する
    error: function({ ... }),
    success: function({ ... }),
    completed: function({ ... })
});

のように「jsonp:」にパラメータ名を指定する必要があることが最初わからなかった。

jsonp:」が指定されない場合

jQueryAJAX を利用するサンプルコードで「jsonp:」に値が設定されていないものを見かけるが、「jsonp:」が設定されていない場合、callback 関数を指定するパラメータ名は、callback が指定される(ややこしい)。
どういうことかというと、例えば「jsonp: JsonCallback」でパラメータ名を明示している場合は、
http://localhost/ajax?query=hoge&type=fuga&JsonCallback=callbackの関数名
となる。では、「jsonp:」でパラメータ名が指定されていない場合は、
http://localhost/ajax?query=hoge&type=fuga&callback=callbackの関数名
というように、「callback」がデフォルトで設定されているようになっている。そのため、Yahoo WEB API のように明示しなくても、JSONP での処理ができる。
だからこそかもしれないが、Bing のように callback のパラメータ名ではない場合、初心者は戸惑うように思う。

callback を指定するパラメータ名とパラメータ値としての「?」

これを調べている際に、「jsonp:」を明示しないで JSONP を処理するための方法を示しているページをいくつか見つけた。
その方法は、

$.ajax({
    // callback 関数を指定するパラメータ名の値に「?」を指定する URL を
    // 「url:」に指定する
    url: 'http://localhost/ajax?JsonCallback=?&',
    dataType: 'jsonp',
    query: 'hoge',
    type: 'fuga',
    data: {
        ...
    },
    error: function({ ... }),
    success: function({ ... }),
    completed: function({ ... })
});

callback 関数を指定するパラメータ名の値に「?」を指定する方法だった。こうすることで、上記と同様に「?」が無名関数に置換されてサーバーにリクエストが送られている。
http://localhost/ajax?JsonCallback=callbackの関数名&query=hoge&type=fuga&
ここまで理解するのに、ほぼ丸一日を費やした…。

雑感

いま思うことは、

  1. Bing API を使っている日本人は少ないのでは…。
  2. Microsoft のリファレンスページは、MS製品を利用するユーザーを対象に作られているように思える。
  3. JSONP ではまっても、たいていの WEB APIXML のレスポンスを用意しているので、逃げ道が用意されている。

から、Bing を jQuery で使おうというサンプルコードが掲載されているページが少なかったのではないかな…。