Dela via


Självstudie: Skapa en enkeltsidesapp med Bing bildsöknings-API:t

Varning

Den 30 oktober 2020 flyttade API:erna för Bing Search från Azure AI-tjänster till Bing Search Services. Den här dokumentationen tillhandahålls endast som referens. Uppdaterad dokumentation finns i dokumentationen för API:et för Bing-sökning. Anvisningar om hur du skapar nya Azure-resurser för Bing-sökning finns i Skapa en Bing Search-resurs via Azure Marketplace.

Med API:et för bildsökning i Bing kan du söka på webben efter relevanta bilder av hög kvalitet. Använd den här handledningen för att skapa en ensidig webbapplikation som kan skicka sökfrågor till API:et och visa resultaten i webbsidan. Den här självstudien liknar den motsvarande självstudie för Bing-webbsökning.

I den här självstudieappen visas hur du:

  • Utföra ett API-anrop för bildsökning i Bing i JavaScript
  • Förbättra sökresultaten med hjälp av sökalternativ
  • Visa och bläddra bland sökresultat
  • Begära och hantera en API-prenumerationsnyckel och Bing-klient-ID.

Förutsättningar

  • Den senaste versionen av Node.js.
  • Det Express.js-ramverket för Node.js. Installationsinstruktioner för källkoden finns i GitHub-exempelfilen readme.

Hantera och lagra användarprenumerationsnycklar

Det här programmet använder webbläsarens beständiga lagring för att lagra API-prenumerationsnycklar. Om ingen nyckel lagras uppmanar webbsidan användaren att ange sin nyckel och lagra den för senare användning. Om nyckeln senare avvisas av API:et kommer appen att ta bort den från lagringen. Det här exemplet använder den globala slutpunkten. Du kan också använda den anpassade underdomänen endpoint som visas i Azure-portalen för din resurs.

Definiera storeValue- och retrieveValue-funktioner för att använda antingen localStorage-objektet (om webbläsaren stöder det) eller en 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();
    }
}

Funktionen getSubscriptionKey() försöker hämta en tidigare lagrad nyckel med hjälp av retrieveValue. Om en inte hittas uppmanas användaren att ange sin nyckel och lagra den med hjälp av 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;
}

HTML-<form>-taggen onsubmit anropar funktionen bingWebSearch för att returnera sökresultat. bingWebSearch använder getSubscriptionKey för att autentisera varje fråga. Som du ser i föregående definition uppmanar getSubscriptionKey användaren att ange nyckeln om nyckeln inte har angetts. Nyckeln lagras sedan för fortsatt användning av programmet.

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

Skicka sökbegäranden

Det här programmet använder en HTML-<form> för att först skicka begäranden om användarsökning med hjälp av attributet onsubmit för att anropa newBingImageSearch().

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

Som standard returnerar onsubmit-hanteraren false, så att formuläret inte skickas.

Välj sökalternativ

[formulär för bildsökning i Bing]

API:et för bildsökning i Bing erbjuder flera filterfrågeparametrar för att begränsa och filtrera sökresultat. HTML-formuläret i det här programmet använder och visar följande parameteralternativ:

Alternativ Beskrivning
where En nedrullningsbar meny för att välja marknaden (plats och språk) som används för sökningen.
query Textfältet där söktermer ska anges.
aspect Radioknappar för att välja proportioner för de hittade bilderna: ungefärliga kvadrater, breda eller höga.
color
when Den nedrullningsbara menyn för att eventuellt begränsa sökningen till den senaste dagen, veckan eller månaden.
safe En kryssruta som anger om du vill använda Bings SafeSearch-funktion för att filtrera bort "vuxna" resultat.
count Dolt fält. Antalet sökresultat som ska returneras för varje begäran. Ändra om du vill visa färre eller fler resultat per sida.
offset Dolt fält. Förskjutningen av det första sökresultatet i begäran, används för paginering. Den återställs till 0 på en ny begäran.
nextoffset Dolt fält. När du får ett sökresultat anges det här fältet till värdet för nextOffset i svaret. Om du använder det här fältet undviker du överlappande resultat på efterföljande sidor.
stack Dolt fält. En JSON-kodad lista över offsetpositionerna för föregående sidor med sökresultat för att navigera tillbaka.

Funktionen bingSearchOptions() formaterar dessa alternativ till en partiell frågesträng som kan användas i appens API-begäranden.

// 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("&");
}

Utför förfrågan

Med hjälp av sökfrågan, alternativsträngen och API-nyckeln använder funktionen BingImageSearch() ett XMLHttpRequest-objekt för att göra begäran till slutpunkten för bildsökning i 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;
}

När HTTP-begäran har slutförts anropar JavaScript händelsehanteraren "load" handleBingResponse() för att hantera en lyckad HTTP GET-begäran.

// 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"));
    }
}

Viktigt!

Lyckade HTTP-begäranden kan innehålla misslyckad sökinformation. Om ett fel uppstår under sökåtgärden returnerar API:et för bildsökning i Bing en HTTP-statuskod som inte är 200 och felinformationen i JSON-svaret. Om begäran var hastighetsbegränsad returnerar API:et dessutom ett tomt svar.

Visa sökresultaten

Sökresultat visas av funktionen renderSearchResults(), som tar JSON som returneras av tjänsten bildsökning i Bing och anropar en lämplig återgivningsfunktion på alla returnerade bilder och relaterade sökningar.

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));
}

Bildsökningsresultat finns i value objekt på den översta nivån i JSON-svaret. Dessa skickas till renderImageResults(), som itererar genom resultaten och konverterar varje objekt till 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");
}

API:et för bildsökning i Bing kan returnera fyra typer av sökförslag som hjälper till att vägleda användarnas sökupplevelser, var och en i sitt eget objekt på den översta nivån:

Förslag Beskrivning
pivotSuggestions Frågor som ersätter ett pivotord i den ursprungliga sökningen med ett annat. Om du till exempel söker efter "röda blommor" kan ett pivotord vara "rött" och ett pivotförslag kan vara "gula blommor".
queryExpansions Frågor som begränsar den ursprungliga sökningen genom att lägga till fler termer. Om du till exempel söker efter "Microsoft Surface" kan en frågeexpansion vara "Microsoft Surface Pro".
relatedSearches Frågor som också har angetts av andra användare som har angett den ursprungliga sökningen. Om du till exempel söker efter "Mount Rainier" kan en relaterad sökning vara "Mt. Sankt Helens."
similarTerms Frågor som liknar den ursprungliga sökningen. Om du till exempel söker efter "kattungar" kan en liknande term vara "söt".

Det här programmet renderar bara relatedItems förslag och placerar de resulterande länkarna i sidans sidofält.

Återgivning av sökresultat

I det här programmet innehåller searchItemRenderers-objektet återgivningsfunktioner som genererar HTML för varje typ av sökresultat.

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

Dessa återgivningsfunktioner accepterar följande parametrar:

Parameter Beskrivning
item JavaScript-objektet som innehåller objektets egenskaper, till exempel dess URL och dess beskrivning.
index Indexet för resultatposten i samlingen.
count Antalet objekt i sökresultatobjektets samling.

Parametrarna index och count används för att numrera resultat, generera HTML för samlingar och organisera innehållet. Mer specifikt:

  • Beräknar miniatyrbildens storlek (bredden varierar, med minst 120 bildpunkter, medan höjden är fast vid 90 bildpunkter).
  • Skapar HTML-<img>-taggen för att visa miniatyrbilden.
  • Skapar HTML-<a> taggar som länkar till bilden och sidan som innehåller den.
  • Skapar beskrivningen som visar information om avbildningen och den webbplats den finns på.
    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

Miniatyrbildens height och width används i både taggen <img> och fälten h och w i miniatyrbildens URL. Detta gör att Bing kan returnera en miniatyrbild av exakt den storleken.

Bevara klient-ID

Svar från API:erna för Bing-sökning kan innehålla en X-MSEdge-ClientID rubrik som ska skickas tillbaka till API:et med efterföljande begäranden. Om flera API:er för Bing-sökning används ska samma klient-ID användas med dem alla, om möjligt.

Om du anger X-MSEdge-ClientID-huvudet kan Bing-API:erna associera alla en användares sökningar, vilket är användbart i

För det första gör det att Bing-sökmotorn kan tillämpa tidigare kontext på sökningar för att hitta resultat som bättre tillfredsställer användaren. Om en användare tidigare har sökt efter termer relaterade till segling, till exempel, kan en senare sökning efter "knutar" företrädesvis returnera information om knutar som används vid segling.

För det andra kan Bing slumpmässigt välja användare för att uppleva nya funktioner innan de blir allmänt tillgängliga. Att tillhandahålla samma klient-ID med varje begäran säkerställer att användare som har valts för att se en funktion alltid ser den. Utan klient-ID:t kan användaren se en funktion visas och försvinna, till synes slumpmässigt, i sökresultaten.

Cors (Browser Security Policies) kan förhindra att X-MSEdge-ClientID-huvudet är tillgängligt för JavaScript. Den här begränsningen inträffar när söksvaret har ett annat ursprung än den sida som begärde det. I en produktionsmiljö bör du hantera den här principen genom att vara värd för ett skript på serversidan som gör API-anropet på samma domän som webbsidan. Eftersom skriptet har samma ursprung som webbsidan är X-MSEdge-ClientID-huvudet sedan tillgängligt för JavaScript.

Anmärkning

I ett produktionswebbprogram bör du utföra begäran på serversidan ändå. Annars måste din API-nyckel för Bing-sökning inkluderas på webbsidan, där den är tillgänglig för alla som visar källan. Du debiteras för all användning under din API-prenumerationsnyckel, även begäranden från obehöriga parter, så det är viktigt att inte exponera din nyckel.

I utvecklingssyfte kan du begära API för webbsökning i Bing via en CORS-proxy. Svaret från en sådan proxy har en Access-Control-Expose-Headers-rubrik som tillåter svarshuvuden och gör dem tillgängliga för JavaScript.

Det är enkelt att installera en CORS-proxy för att låta vår app för självstudier komma åt klient-ID-headern. Först, om du inte redan har det, installera Node.js. Utfärda sedan följande kommando i ett kommandofönster:

npm install -g cors-proxy-server

Ändra sedan slutpunkten för webbsökning i Bing i HTML-filen till:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search

Starta slutligen CORS-proxyn med följande kommando:

cors-proxy-server

Låt kommandofönstret vara öppet medan du använder självstudieappen. om du stänger fönstret stoppas proxyn. I avsnittet med utökningsbara HTTP-huvuden under sökresultatet kan du nu se X-MSEdge-ClientID-huvudet (bland annat) och kontrollera att det är samma för varje begäran.

Nästa steg

Se även