Condividi tramite


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

[modulo Ricerca immagini Bing]

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.

Passaggi successivi

Vedere anche