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
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.