Tutorial: Membuat aplikasi satu halaman menggunakan Bing Image Search API

Peringatan

Pada 30 Oktober 2020, API Bing Search dipindahkan dari layanan Azure AI ke Bing Search Services. Dokumentasi ini disediakan hanya untuk referensi. Untuk dokumentasi terbaru, lihat dokumentasi Bing Search API. Untuk petunjuk tentang cara membuat sumber daya Azure baru untuk pencarian Bing, lihat Membuat sumber daya Pencarian Bing melalui Marketplace Azure.

Bing Image Search API memungkinkan Anda mencari gambar yang relevan dan berkualitas tinggi di web. Gunakan tutorial ini untuk membuat aplikasi web satu halaman yang dapat mengirim kueri pencarian ke API, dan menampilkan hasilnya di dalam halaman web. Tutorial ini mirip dengan tutorial terkait untuk Bing Web Search.

Aplikasi tutorial mengilustrasikan cara:

  • Melakukan panggilan Bing Image Search API di JavaScript
  • Meningkatkan hasil pencarian menggunakan opsi pencarian
  • Menampilkan dan memberi nomor melalui hasil pencarian
  • Minta dan tangani kunci langganan API, dan ID klien Bing.

Prasyarat

  • Versi terbaru Node.js.
  • Kerangka kerja Express.js untuk Node.js. Instruksi penginstalan untuk kode sumber tersedia dalam sampel file readme GitHub.

Mengelola dan menyimpan kunci langganan pengguna

Aplikasi ini menggunakan penyimpanan persisten browser web untuk menyimpan kunci langganan API. Jika tidak ada kunci yang disimpan, halaman web akan meminta pengguna untuk memasukkan kunci mereka dan menyimpannya untuk digunakan nanti. Jika kunci nantinya ditolak oleh API, Aplikasi akan menghapusnya dari penyimpanan. Sampel ini menggunakan titik akhir global. Anda juga dapat menggunakan titik akhir subdomain kustom yang ditampilkan di portal Azure untuk sumber daya Anda.

Tentukan fungsi storeValue dan retrieveValue untuk menggunakan objek localStorage (jika browser mendukungnya) atau cookie.

// Cookie names for data being stored
API_KEY_COOKIE   = "bing-search-api-key";
CLIENT_ID_COOKIE = "bing-search-client-id";
// The Bing Image Search API endpoint
BING_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/images/search";

try { //Try to use localStorage first
    localStorage.getItem;   

    window.retrieveValue = function (name) {
        return localStorage.getItem(name) || "";
    }
    window.storeValue = function(name, value) {
        localStorage.setItem(name, value);
    }
} catch (e) {
    //If the browser doesn't support localStorage, try a cookie
    window.retrieveValue = function (name) {
        var cookies = document.cookie.split(";");
        for (var i = 0; i < cookies.length; i++) {
            var keyvalue = cookies[i].split("=");
            if (keyvalue[0].trim() === name) return keyvalue[1];
        }
        return "";
    }
    window.storeValue = function (name, value) {
        var expiry = new Date();
        expiry.setFullYear(expiry.getFullYear() + 1);
        document.cookie = name + "=" + value.trim() + "; expires=" + expiry.toUTCString();
    }
}

Fungsi getSubscriptionKey() mencoba mengambil kunci yang disimpan sebelumnya menggunakan retrieveValue. Jika tidak ditemukan, itu akan meminta pengguna untuk memasukkan kunci mereka, dan menyimpannya menggunakan storeValue.


// Get the stored API subscription key, or prompt if it's not found
function getSubscriptionKey() {
    var key = retrieveValue(API_KEY_COOKIE);
    while (key.length !== 32) {
        key = prompt("Enter Bing Search API subscription key:", "").trim();
    }
    // always set the cookie in order to update the expiration date
    storeValue(API_KEY_COOKIE, key);
    return key;
}

Tag <form> HTML onsubmit memanggil fungsi bingWebSearch untuk mengembalikan hasil pencarian. bingWebSearch menggunakan getSubscriptionKey untuk mengautentikasi setiap kueri. Seperti yang ditunjukkan pada definisi sebelumnya, getSubscriptionKey meminta kunci kepada pengguna jika kunci belum dimasukkan. Kunci kemudian disimpan untuk melanjutkan penggunaan oleh aplikasi.

<form name="bing" onsubmit="this.offset.value = 0; return bingWebSearch(this.query.value,
bingSearchOptions(this), getSubscriptionKey())">

Mengirim permintaan pencarian

Aplikasi ini menggunakan HTML <form> untuk awalnya mengirim permintaan pencarian pengguna, menggunakan atribut onsubmit untuk memanggil newBingImageSearch().

<form name="bing" onsubmit="return newBingImageSearch(this)">

Secara default, handler onsubmit mengembalikan false, agar formulir tidak dikirimkan.

Memilih opsi pencarian

[Formulir Bing Image Search]

Bing Image Search API menawarkan beberapa parameter kueri filter untuk mempersempit dan memfilter hasil pencarian. Formulir HTML dalam aplikasi ini menggunakan dan menampilkan opsi parameter berikut:

Opsi Deskripsi
where Menu drop-down untuk memilih pasar (lokasi dan bahasa) yang digunakan untuk pencarian.
query Bidang teks untuk memasukkan istilah pencarian.
aspect Tombol radio untuk memilih proporsi gambar yang ditemukan: kira-kira persegi, lebar, atau tinggi.
color
when Menu drop-down untuk membatasi pencarian secara opsional ke hari, minggu, atau bulan terbaru.
safe Kotak centang yang menunjukkan apakah akan menggunakan fitur SafeSearch Bing untuk memfilter hasil "dewasa".
count Bidang tersembunyi. Jumlah hasil pencarian yang akan dikembalikan pada setiap permintaan. Ubah untuk menampilkan lebih sedikit atau lebih banyak hasil per halaman.
offset Bidang tersembunyi. Offset hasil pencarian pertama dalam permintaan; digunakan untuk penomoran. Ini diatur ulang ke 0 pada permintaan baru.
nextoffset Bidang tersembunyi. Setelah menerima hasil pencarian, bidang ini diatur ke nilai nextOffset dalam respons. Menggunakan bidang ini menghindari hasil yang tumpang tindih pada halaman yang berurutan.
stack Bidang tersembunyi. Daftar offset halaman hasil pencarian sebelumnya yang dikodekan JSON, untuk menavigasi kembali ke halaman sebelumnya.

Fungsi bingSearchOptions() memformat opsi ini menjadi string kueri parsial, yang dapat digunakan dalam permintaan API aplikasi.

// Build query options from the HTML form
function bingSearchOptions(form) {

    var options = [];
    options.push("mkt=" + form.where.value);
    options.push("SafeSearch=" + (form.safe.checked ? "strict" : "off"));
    if (form.when.value.length) options.push("freshness=" + form.when.value);
    var aspect = "all";
    for (var i = 0; i < form.aspect.length; i++) {
        if (form.aspect[i].checked) {
            aspect = form.aspect[i].value;
            break;
        }
    }
    options.push("aspect=" + aspect);
    if (form.color.value) options.push("color=" + form.color.value);
    options.push("count=" + form.count.value);
    options.push("offset=" + form.offset.value);
    return options.join("&");
}

Melakukan permintaan

Menggunakan kueri pencarian, string opsi, dan kunci API, fungsi BingImageSearch() menggunakan objek XMLHttpRequest untuk membuat permintaan ke titik akhir Bing Image Search.

// perform a search given query, options string, and API key
function bingImageSearch(query, options, key) {

    // scroll to top of window
    window.scrollTo(0, 0);
    if (!query.trim().length) return false;     // empty query, do nothing

    showDiv("noresults", "Working. Please wait.");
    hideDivs("results", "related", "_json", "_http", "paging1", "paging2", "error");

    var request = new XMLHttpRequest();
    var queryurl = BING_ENDPOINT + "?q=" + encodeURIComponent(query) + "&" + options;

    // open the request
    try {
        request.open("GET", queryurl);
    }
    catch (e) {
        renderErrorMessage("Bad request (invalid URL)\n" + queryurl);
        return false;
    }

    // add request headers
    request.setRequestHeader("Ocp-Apim-Subscription-Key", key);
    request.setRequestHeader("Accept", "application/json");
    var clientid = retrieveValue(CLIENT_ID_COOKIE);
    if (clientid) request.setRequestHeader("X-MSEdge-ClientID", clientid);

    // event handler for successful response
    request.addEventListener("load", handleBingResponse);

    // event handler for erorrs
    request.addEventListener("error", function() {
        renderErrorMessage("Error completing request");
    });

    // event handler for aborted request
    request.addEventListener("abort", function() {
        renderErrorMessage("Request aborted");
    });

    // send the request
    request.send();
    return false;
}

Setelah berhasil menyelesaikan permintaan HTTP, JavaScript akan memanggil penanganan aktivitas "pemuatan" handleBingResponse() untuk menangani permintaan HTTP GET yang berhasil.

// handle Bing search request results
function handleBingResponse() {
    hideDivs("noresults");

    var json = this.responseText.trim();
    var jsobj = {};

    // try to parse JSON results
    try {
        if (json.length) jsobj = JSON.parse(json);
    } catch(e) {
        renderErrorMessage("Invalid JSON response");
    }

    // show raw JSON and HTTP request
    showDiv("json", preFormat(JSON.stringify(jsobj, null, 2)));
    showDiv("http", preFormat("GET " + this.responseURL + "\n\nStatus: " + this.status + " " +
        this.statusText + "\n" + this.getAllResponseHeaders()));

    // if HTTP response is 200 OK, try to render search results
    if (this.status === 200) {
        var clientid = this.getResponseHeader("X-MSEdge-ClientID");
        if (clientid) retrieveValue(CLIENT_ID_COOKIE, clientid);
        if (json.length) {
            if (jsobj._type === "Images") {
                if (jsobj.nextOffset) document.forms.bing.nextoffset.value = jsobj.nextOffset;
                renderSearchResults(jsobj);
            } else {
                renderErrorMessage("No search results in JSON response");
            }
        } else {
            renderErrorMessage("Empty response (are you sending too many requests too quickly?)");
        }
    }

    // Any other HTTP response is an error
    else {
        // 401 is unauthorized; force re-prompt for API key for next request
        if (this.status === 401) invalidateSubscriptionKey();

        // some error responses don't have a top-level errors object, so gin one up
        var errors = jsobj.errors || [jsobj];
        var errmsg = [];

        // display HTTP status code
        errmsg.push("HTTP Status " + this.status + " " + this.statusText + "\n");

        // add all fields from all error responses
        for (var i = 0; i < errors.length; i++) {
            if (i) errmsg.push("\n");
            for (var k in errors[i]) errmsg.push(k + ": " + errors[i][k]);
        }

        // also display Bing Trace ID if it isn't blocked by CORS
        var traceid = this.getResponseHeader("BingAPIs-TraceId");
        if (traceid) errmsg.push("\nTrace ID " + traceid);

        // and display the error message
        renderErrorMessage(errmsg.join("\n"));
    }
}

Penting

Permintaan HTTP yang berhasil mungkin berisi informasi pencarian yang gagal. Jika terjadi kesalahan selama operasi pencarian, Bing Image Search API akan mengembalikan kode status HTTP non-200 dan informasi kesalahan dalam respons JSON. Selain itu, jika permintaan dibatasi tarifnya, API akan mengembalikan respons kosong.

Menampilkan hasil pencarian

Hasil pencarian ditampilkan oleh fungsi renderSearchResults(), yang mengambil JSON yang dikembalikan oleh layanan Bing Image Search dan memanggil fungsi perender yang sesuai pada setiap gambar yang dikembalikan dan pencarian terkait.

function renderSearchResults(results) {

    // add Prev / Next links with result count
    var pagingLinks = renderPagingLinks(results);
    showDiv("paging1", pagingLinks);
    showDiv("paging2", pagingLinks);

    showDiv("results", renderImageResults(results.value));
    if (results.relatedSearches)
        showDiv("sidebar", renderRelatedItems(results.relatedSearches));
}

Hasil pencarian gambar terdapat di objek value tingkat atas dalam respons JSON. Ini diteruskan ke renderImageResults(), yang melakukan iterasi hasil dan mengonversi setiap item menjadi HTML.

function renderImageResults(items) {
    var len = items.length;
    var html = [];
    if (!len) {
        showDiv("noresults", "No results.");
        hideDivs("paging1", "paging2");
        return "";
    }
    for (var i = 0; i < len; i++) {
        html.push(searchItemRenderers.images(items[i], i, len));
    }
    return html.join("\n\n");
}

Bing Image Search API dapat menampilkan empat jenis saran pencarian untuk membantu memandu pengalaman pencarian pengguna, masing-masing dalam objek tingkat atasnya sendiri:

Saran Deskripsi
pivotSuggestions Kueri yang menggantikan kata pivot dalam pencarian asli dengan kata lain. Misalnya, jika Anda mencari "bunga merah", kata pivotnya mungkin adalah "merah", dan saran pivotnya mungkin adalah "bunga kuning."
queryExpansions Kueri yang mempersempit pencarian asli dengan menambahkan lebih banyak istilah. Misalnya, jika Anda mencari "Microsoft Surface", ekspansi kuerinya mungkin adalah "Microsoft Surface Pro."
relatedSearches Kueri yang juga telah dimasukkan oleh pengguna lain yang memasukkan pencarian asli. Misalnya, jika Anda mencari "Mount Rainier", pencarian terkaitnya mungkin adalah "Mt. Saint Helens."
similarTerms Kueri yang mirip artinya dengan pencarian asli. Misalnya, jika Anda mencari "anak kucing," istilah serupa mungkin "lucu".

Aplikasi ini hanya merender relatedItems saran, dan menempatkan tautan yang dihasilkan di bilah sisi halaman.

Merender hasil pencarian

Dalam aplikasi ini, objek searchItemRenderers berisi fungsi perender yang menghasilkan HTML untuk setiap jenis hasil pencarian.

searchItemRenderers = {
    images: function(item, index, count) { ... },
    relatedSearches: function(item) { ... }
}

Fungsi perender ini menerima parameter berikut:

Parameter Deskripsi
item Objek JavaScript yang berisi properti item, seperti URL dan deskripsinya.
index Indeks item hasil dalam koleksinya.
count Jumlah item dalam koleksi item hasil pencarian.

Parameter index dan count digunakan untuk memberi nomor pada hasil, menghasilkan HTML untuk koleksi, dan mengatur konten. Khususnya, parameter itu:

  • Menghitung ukuran gambar mini gambar (lebar bervariasi, dengan minimum 120 piksel, sedangkan tinggi ditetapkan pada 90 piksel).
  • Membuat tag <img> HTML untuk menampilkan gambar mini.
  • Membangun tag <a> HTML yang ditautkan ke gambar dan halaman tempat gambar berada.
  • Membuat deskripsi yang menampilkan informasi tentang gambar dan situs tempatnya berada.
    images: function (item, index, count) {
        var height = 120;
        var width = Math.max(Math.round(height * item.thumbnail.width / item.thumbnail.height), 120);
        var html = [];
        if (index === 0) html.push("<p class='images'>");
        var title = escape(item.name) + "\n" + getHost(item.hostPageDisplayUrl);
        html.push("<p class='images' style='max-width: " + width + "px'>");
        html.push("<img src='"+ item.thumbnailUrl + "&h=" + height + "&w=" + width +
            "' height=" + height + " width=" + width + "'>");
        html.push("<br>");
        html.push("<nobr><a href='" + item.contentUrl + "'>Image</a> - ");
        html.push("<a href='" + item.hostPageUrl + "'>Page</a></nobr><br>");
        html.push(title.replace("\n", " (").replace(/([a-z0-9])\.([a-z0-9])/g, "$1.<wbr>$2") + ")</p>");
        return html.join("");
    }, // relatedSearches renderer omitted

height dan width gambar mini digunakan di tag <img> dan bidang h dan w di URL gambar mini. Ini memungkinkan Bing untuk mengembalikan gambar mini dengan ukuran yang sama persis.

ID klien tetap

Respons dari Bing Search API dapat menyertakan header X-MSEdge-ClientID yang harus dikirim kembali ke API dengan permintaan yang berurutan. Jika beberapa Bing Search API digunakan, ID klien yang sama harus digunakan dengan semuanya, jika memungkinkan.

Menyediakan header X-MSEdge-ClientID memungkinkan Bing API untuk mengaitkan semua pencarian pengguna, yang berguna dalam

Pertama, memungkinkan mesin cari Bing menerapkan konteks sebelumnya ke pencarian untuk menemukan hasil yang lebih memuaskan bagi pengguna. Jika pengguna sebelumnya telah mencari istilah yang terkait dengan berlayar, misalnya, pencarian berikutnya untuk "simpul" akan lebih disukai jika mengembalikan informasi tentang simpul yang digunakan dalam berlayar.

Kedua, Bing dapat secara acak memilih pengguna untuk mencoba fitur baru sebelum fitur tersebut tersedia secara luas. Memberikan ID klien yang sama dengan setiap permintaan memastikan bahwa pengguna yang terpilih untuk melihat fitur, akan selalu melihat fitur tersebut. Tanpa ID klien, pengguna mungkin melihat fitur muncul dan menghilang, tampaknya secara acak, dalam hasil pencarian mereka.

Kebijakan keamanan browser (CORS) dapat mencegah header X-MSEdge-ClientID tersedia di JavaScript. Pembatasan ini terjadi ketika respons pencarian memiliki asal yang berbeda dari halaman yang memintanya. Dalam lingkungan produksi, Anda harus menangani kebijakan ini dengan menghosting skrip sisi server yang melakukan panggilan API pada domain yang sama dengan halaman Web. Karena skrip memiliki asal yang sama dengan halaman Web, header X-MSEdge-ClientID kemudian tersedia untuk JavaScript.

Catatan

Dalam aplikasi Web produksi, Anda harus tetap melakukan permintaan sisi server. Jika tidak, kunci Bing Search API Anda harus disertakan di halaman Web, yang tersedia bagi siapa saja yang melihat sumber. Anda ditagih untuk semua penggunaan pada kunci langganan API, bahkan permintaan yang dibuat oleh pihak yang tidak berwenang, jadi penting untuk tidak menunjukkan kunci Anda.

Untuk tujuan pengembangan, Anda dapat membuat permintaan Bing Web Search API melalui proksi CORS. Respons dari proksi semacam itu memiliki header Access-Control-Expose-Headers yang mengizinkan header respons dan menyediakannya untuk JavaScript.

Sangat mudah untuk memasang proksi CORS untuk mengizinkan aplikasi tutorial kami mengakses header ID klien. Pertama, jika Anda belum memilikinya, pasang Node.js. Kemudian, keluarkan perintah berikut di jendela perintah:

npm install -g cors-proxy-server

Selanjutnya, ubah titik akhir Bing Web Search dalam file HTML menjadi:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search

Terakhir, mulai proksi CORS dengan perintah berikut:

cors-proxy-server

Biarkan jendela perintah terbuka saat Anda menggunakan aplikasi tutorial; menutup jendela akan menghentikan proksi. Di bagian Header HTTP yang dapat diluaskan di bawah hasil pencarian, Anda sekarang dapat melihat header X-MSEdge-ClientID (di antara yang lain) dan memverifikasi bahwa header tersebut sama untuk setiap permintaan.

Langkah berikutnya

Lihat juga