Esercitazione: Creare un'app a pagina singola usando l'API Ricerca immagini Bing
Avvertimento
Il 30 ottobre 2020, le API di ricerca Bing sono state spostate dai servizi di intelligenza artificiale di Azure ai servizi di ricerca Bing. Questa documentazione è disponibile solo per riferimento. Per la documentazione aggiornata, vedere la Documentazione dell'API di Ricerca Bing. Per istruzioni sulla creazione di nuove risorse di Azure per Ricerca Bing, vedere Creare una risorsa di Ricerca Bing tramite Azure Marketplace.
L'API Ricerca immagini Bing consente di cercare immagini pertinenti di alta qualità nel Web. Usare questa esercitazione per creare un'applicazione Web a pagina singola in grado di inviare query di ricerca all'API e visualizzare i risultati all'interno della pagina Web. Questa esercitazione è simile alla esercitazione corrispondente della Ricerca Web Bing.
L'app del tutorial illustra come:
- Eseguire una chiamata API Ricerca immagini Bing in JavaScript
- Migliorare i risultati della ricerca usando le opzioni di ricerca
- Visualizzare e scorrere i risultati della ricerca
- Richiedere e gestire una chiave di sottoscrizione API e un ID client Bing.
Prerequisiti
- Versione più recente di Node.js.
- Framework di Express.js per Node.js. Le istruzioni di installazione per il codice sorgente sono disponibili nel file leggimi di esempio di GitHub.
Gestire e archiviare le chiavi di sottoscrizione utente
Questa applicazione usa l'archiviazione permanente dei Web browser per archiviare le chiavi di sottoscrizione api. Se non viene archiviata alcuna chiave, la pagina Web richiederà all'utente la chiave e la archivierà per usarla in un secondo momento. Se la chiave viene rifiutata in un secondo momento dall'API, l'app lo rimuoverà dall'archiviazione. Questo esempio usa l'endpoint globale. È anche possibile usare il sottodominio personalizzato endpoint visualizzato nel portale di Azure per la risorsa.
Definire storeValue
e retrieveValue
funzioni per usare l'oggetto localStorage
(se il browser lo supporta) o un 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();
}
}
La funzione getSubscriptionKey()
tenta di recuperare una chiave archiviata in precedenza usando retrieveValue
. Se non viene trovato, richiederà all'utente la chiave e la archivierà usando 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;
}
Il tag <form>
HTML onsubmit
chiama la funzione bingWebSearch
per restituire i risultati della ricerca.
bingWebSearch
utilizza getSubscriptionKey
per autenticare ogni interrogazione. Come illustrato nella definizione precedente, getSubscriptionKey
richiede all'utente la chiave se la chiave non è stata immessa. La chiave viene quindi archiviata per continuare l'uso da parte dell'applicazione.
<form name="bing" onsubmit="this.offset.value = 0; return bingWebSearch(this.query.value,
bingSearchOptions(this), getSubscriptionKey())">
Inviare richieste di ricerca
Questa applicazione utilizza un <form>
HTML per inviare inizialmente le richieste di ricerca degli utenti, utilizzando l'attributo onsubmit
per chiamare newBingImageSearch()
.
<form name="bing" onsubmit="return newBingImageSearch(this)">
Per impostazione predefinita, il gestore onsubmit
restituisce false
, impedendo l'invio del modulo.
Selezionare le opzioni di ricerca
L'API Ricerca immagini Bing offre diversi parametri di query di filtro per restringere e filtrare i risultati della ricerca. Il modulo HTML in questa applicazione usa e visualizza le opzioni di parametro seguenti:
Opzione | Descrizione |
---|---|
where |
Menu a discesa per selezionare il mercato (località e lingua) usato per la ricerca. |
query |
Campo di testo in cui immettere i termini di ricerca. |
aspect |
Pulsanti di opzione per scegliere le proporzioni delle immagini trovate: approssimativamente quadrate, larghe o alte. |
color |
|
when |
Menu a discesa per limitare facoltativamente la ricerca al giorno, alla settimana o al mese più recenti. |
safe |
Casella di controllo che indica se usare la funzionalità SafeSearch di Bing per filtrare i risultati "per adulti". |
count |
Campo nascosto. Numero di risultati della ricerca da restituire in ogni richiesta. Modificare per visualizzare meno o più risultati per pagina. |
offset |
Campo nascosto. Offset del primo risultato della ricerca nella richiesta; utilizzato per la paginazione. Viene reimpostato su 0 con una nuova richiesta. |
nextoffset |
Campo nascosto. Al momento della ricezione di un risultato della ricerca, questo campo viene impostato sul valore del nextOffset nella risposta. L'uso di questo campo evita la sovrapposizione dei risultati nelle pagine successive. |
stack |
Campo nascosto. Elenco con codifica JSON degli offset delle pagine precedenti dei risultati della ricerca, per tornare alle pagine precedenti. |
La funzione bingSearchOptions()
formatta queste opzioni in una stringa di query parziale, che può essere usata nelle richieste API dell'app.
// 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("&");
}
Esecuzione della richiesta
Usando la query di ricerca, la stringa di opzioni e la chiave API, la funzione BingImageSearch()
usa un oggetto XMLHttpRequest per effettuare la richiesta all'endpoint di Ricerca immagini Bing.
// 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;
}
Al termine della richiesta HTTP, JavaScript chiama il gestore eventi "load" handleBingResponse()
per gestire una richiesta HTTP GET riuscita.
// 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"));
}
}
Importante
Le richieste HTTP riuscite possono contenere informazioni di ricerca non riuscite. Se si verifica un errore durante l'operazione di ricerca, l'API Ricerca immagini Bing restituirà un codice di stato HTTP non 200 e informazioni sull'errore nella risposta JSON. Inoltre, se la richiesta era limitata alla frequenza, l'API restituirà una risposta vuota.
Visualizzare i risultati della ricerca
I risultati della ricerca vengono visualizzati dalla funzione renderSearchResults()
, che accetta il codice JSON restituito dal servizio Ricerca immagini Bing e chiama una funzione renderer appropriata su eventuali immagini restituite e ricerche correlate.
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));
}
I risultati della ricerca di immagini sono contenuti nell'oggetto value
di primo livello all'interno della risposta JSON. Questi vengono passati a renderImageResults()
, che scorre i risultati e converte ogni elemento in 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");
}
L'API Ricerca immagini Bing può restituire quattro tipi di suggerimenti di ricerca per guidare le esperienze di ricerca degli utenti, ognuna nel proprio oggetto di primo livello:
Suggerimento | Descrizione |
---|---|
pivotSuggestions |
Query che sostituiscono una parola chiave nella ricerca originale con un'altra diversa. Ad esempio, se si cerca "fiori rossi", una parola pivot potrebbe essere "rossa" e un suggerimento pivot potrebbe essere "fiori gialli". |
queryExpansions |
Query che restringeno la ricerca originale aggiungendo altri termini. Ad esempio, se si cerca "Microsoft Surface", un'espansione della query potrebbe essere "Microsoft Surface Pro". |
relatedSearches |
Query immesse anche da altri utenti che hanno immesso la ricerca originale. Ad esempio, se si cerca "Mount Rainier", una ricerca correlata potrebbe essere "Mt. Sant'Helens." |
similarTerms |
Query con un significato simile a quello della ricerca originale. Ad esempio, se cerchi "gattini", un termine simile potrebbe essere "carino". |
Questa applicazione esegue solo il rendering dei suggerimenti relatedItems
e inserisce i collegamenti risultanti nella barra laterale della pagina.
Visualizzazione dei risultati della ricerca
In questa applicazione, l'oggetto searchItemRenderers
contiene funzioni renderer che generano codice HTML per ogni tipo di risultato della ricerca.
searchItemRenderers = {
images: function(item, index, count) { ... },
relatedSearches: function(item) { ... }
}
Queste funzioni renderer accettano i parametri seguenti:
Parametro | Descrizione |
---|---|
item |
Oggetto JavaScript contenente le proprietà dell'elemento, ad esempio il relativo URL e la relativa descrizione. |
index |
Indice dell'elemento del risultato all'interno della raccolta. |
count |
Numero di elementi nella raccolta dell'elemento del risultato di ricerca. |
I parametri index
e count
vengono usati per numerare i risultati, generare codice HTML per le raccolte e organizzare il contenuto. In particolare, esso:
- Calcola le dimensioni dell'anteprima dell'immagine (la larghezza varia, con un minimo di 120 pixel, mentre l'altezza è fissa a 90 pixel).
- Compila il tag html
<img>
per visualizzare l'anteprima dell'immagine. - Compila i tag html
<a>
che si collegano all'immagine e alla pagina che lo contiene. - Crea la descrizione che visualizza informazioni sull'immagine e sul sito su cui si trova.
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
I height
e i width
dell'immagine di anteprima vengono usati sia nel tag <img>
che nei campi h
e w
nell'URL dell'anteprima. In questo modo Bing restituisce una miniatura di esattamente quella dimensione.
ID client persistente
Le risposte dalle API di ricerca Bing possono includere un'intestazione X-MSEdge-ClientID
che deve essere inviata all'API con richieste successive. Se vengono usate più API di ricerca Bing, è consigliabile usare lo stesso ID client con tutte queste API, se possibile.
Fornire l'intestazione X-MSEdge-ClientID
consente alle API Bing di associare tutte le ricerche di un utente, utile in
In primo luogo, consente al motore di ricerca Bing di applicare il contesto passato alle ricerche per trovare risultati che soddisfano meglio l'utente. Se in precedenza un utente ha cercato termini relativi alla navigazione, ad esempio, una ricerca successiva di "nodi" potrebbe restituire preferibilmente informazioni sui nodi utilizzati nella vela.
In secondo luogo, Bing può selezionare in modo casuale gli utenti per sperimentare nuove funzionalità prima che vengano rese ampiamente disponibili. Fornire lo stesso ID client con ogni richiesta garantisce che gli utenti scelti per visualizzare una funzionalità lo vedano sempre. Senza l'ID client, l'utente potrebbe visualizzare una funzionalità e scomparire, apparentemente in modo casuale, nei risultati della ricerca.
I criteri di sicurezza del browser (CORS) potrebbero impedire che l'intestazione X-MSEdge-ClientID
sia disponibile per JavaScript. Questa limitazione si verifica quando la risposta di ricerca ha un'origine diversa dalla pagina che l'ha richiesta. In un ambiente di produzione, è necessario gestire questo criterio ospitando uno script lato server che esegue la chiamata API nello stesso dominio della pagina Web. Poiché lo script ha la stessa origine della pagina Web, l'intestazione X-MSEdge-ClientID
è quindi disponibile per JavaScript.
Nota
In un'applicazione Web di produzione, è necessario eseguire comunque il lato server di richiesta. In caso contrario, la chiave API Ricerca Bing deve essere inclusa nella pagina Web, in cui è disponibile per chiunque visualizzi il codice sorgente. Vengono fatturati tutti gli utilizzi con la chiave di sottoscrizione API, anche le richieste effettuate da parti non autorizzate, quindi è importante non esporre la chiave.
A scopo di sviluppo, è possibile effettuare la richiesta dell'API Ricerca Web Bing tramite un proxy CORS. La risposta da tale proxy ha un'intestazione Access-Control-Expose-Headers
che consente le intestazioni di risposta e le rende disponibili per JavaScript.
È facile installare un proxy CORS per consentire all'app tutorial di accedere all'intestazione dell'ID client. Per prima cosa, se non ce l'hai già, installi Node.js. Eseguire quindi il comando seguente in una finestra di comando:
npm install -g cors-proxy-server
Modificare quindi l'endpoint di Ricerca Web Bing nel file HTML in:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search
Avviare infine il proxy CORS con il comando seguente:
cors-proxy-server
Lasciare aperta la finestra di comando mentre si usa l'app dell'esercitazione; la chiusura della finestra arresta il proxy. Nella sezione Intestazioni HTTP espandibili sotto i risultati della ricerca è ora possibile visualizzare l'intestazione X-MSEdge-ClientID
(tra le altre) e verificare che sia la stessa per ogni richiesta.