Dela via


Självstudier: Skapa en ensidesapp med hjälp av API för bildsökning i Bing

Varning

Den 30 oktober 2020 flyttade Bing-sökning API:er från Azure AI-tjänster till Bing-sökning 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-sökning resurs via Azure Marketplace.

Med API för bildsökning i Bing kan du söka på webben efter relevanta bilder med hög kvalitet. Använd den här självstudien för att skapa ett enkelsidigt program som kan skicka sökfrågor till API:et och visa resultaten inom webbsidan. Den här självstudiekursen liknar motsvarande självstudiekurs för webbsökning i Bing.

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

  • Anropa API för bildsökning i Bing i JavaScript
  • Förbättra sökresultat med hjälp av alternativ för sökning
  • Visa och bläddra igenom sökresultat
  • Begära och hantera en API-prenumerationsnyckel och klient-ID för Bing.

Förutsättningar

  • Den senaste versionen av Node.js.
  • Ramverket Express.js för Node.js. Installationsinstruktioner för källkoden finns i GitHub-exemplets readme-fil.

Hantera och lagra användarens prenumerationsnycklar

Det här programmet använder webbläsarens beständiga lagring för att lagra prenumerationsnycklar för API:et. Om ingen nyckel lagras frågar webbsidan användaren om nyckeln och lagrar den för senare användning. Om nyckeln senare avvisas av API appen tas den bort från lagringen. Det här exemplet använder den globala slutpunkten. Du kan också använda den anpassade underdomänslutpunkten som visas i Azure Portal för resursen.

Vi definierar funktionerna storeValue och retrieveValue, antingen med objektet localStorage (inte i alla webbläsare) 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. Det inte går att hitta en nyckel kommer användaren att uppmanas att ange en nyckel som lagras med 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-taggen <form>onsubmit anropar bingWebSearch-funktionen för att returnera sökresultat. bingWebSearch använder getSubscriptionKey för att autentisera varje fråga. Som du ser i den föregående definitionen ber getSubscriptionKey användaren om nyckeln om nyckeln inte har registrerats. Nyckeln lagras sedan för fortlöpande användning av programmet.

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

Skicka sökförfrågningar

Det här programmet använder ett HTML <form> för att inledningsvis skicka användarsökförfrågningar med hjälp av attributet onsubmit för att anropa newBingImageSearch().

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

onsubmit-hanteraren returnerar false, som ser till att formuläret inte skickas.

Välj sökalternativ

[Bildsökformulär i Bing]

Sökning i Bing tillhandahåller flera filtrerfrågeparametrar för att begränsa och filtrera sökresultaten. HTML-formulär i det här programmet använder och visar följande parameteralternativ:

Alternativ Beskrivning
where En listruta för att välja marknad (plats och språk) som används för sökningen.
query Textfältet för att ange sökvillkor.
aspect Alternativknapparna för att välja proportioner för den hittade bilden: ungefär kvadratisk, bred eller hög.
color
when Listruta för att valfritt begränsa sökningen till den senaste dagen, veckan eller månaden.
safe En kryssruta som anger om du vill använda Bing SafeSearch-funktionen för att filtrera bort ”vuxeninnehåll”.
count Dolt fält. Antal sökresultat som 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 växling. Den återställs till 0 vid en ny begäran.
nextoffset Dolt fält. Vid mottagning av ett sökresultat anges fältet som värdet för nextOffset i svaret. Med hjälp av det här fältet undviks överlappande resultat på efterföljande sidor.
stack Dolt fält. En JSON-kodad lista med förskjutningar i de föregående sidorna med sökresultat för att gå tillbaka till föregående sidor.

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öra förfrågan

Beroende på sökfrågan, alternativsträngen och API-nyckeln använder funktionen BingImageSearch() ett XMLHttpRequest-objekt för att göra begäran till Bing-bildsökningsslutpunkten.

// 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 handleBingResponse()-belastningshändelsehanteraren, 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 information om misslyckade sökningar. Om ett fel uppstår i sökåtgärden returnerar API för bildsökning i Bing en icke-200-HTTP-statuskod och felinformation i JSON-svaret. Om begäran var begränsad returnerar API:et ett tomt svar.

Visa sökresultat

Sökresultaten visas som funktionen renderSearchResults(), vilket tar den JSON som returneras av tjänsten för bildsökning i Bing och anropar en lämplig återgivningsfunktion på 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));
}

Sökresultaten returneras som value-objekt på den översta nivån i JSON-svaret. Dessa skickas till renderImageResults(), som går igenom resultaten och konverterar varje objekt till HTML-format.

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 för bildsökning i Bing kan returnera fyra typer av sökförslag för att vägleda användarnas sökmiljöer, var och en i den översta objektnivån:

Förslag Description
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öda”, 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 exempelvis 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 registrerade den ursprungliga sökningen. Om du till exempel söker efter ”Mount Rainier” kan en relaterad sökning vara ”Mt. Saint Helens.”
similarTerms Frågor vars innebörd liknar den ursprungliga sökningen. Om du exempelvis söker efter ”kattungar” kan en liknande term vara ”gulliga”.

Med detta program återges endast relatedItems-förslag och de resulterande länkarna placeras i sidans sidopanel.

Återgivning av sökresultat

I 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) { ... }
}

En funktion för återgivning kan acceptera följande parametrar:

Parameter Beskrivning
item JavaScript-objekt som innehåller objektets egenskaper, som dess webbadress och en beskrivning.
index Index för resultatobjektet i en samling.
count Antal objekt i sökresultatets objektsamling.

Parametrarna index och count används för att numrera resultat, generera HTML-kod för samlingar och ordna innehåll. Mer specifikt:

  • Beräknar storleken på miniatyrbilderna (bredd varierar med minst 120 bildpunkter medan höjden högst får vara 90 bildpunkter).
  • Skapar HTML <img>-taggen för att visa miniatyrbilden.
  • Skapar HTML <a>-taggar som länkar till bilden och den sida som innehåller den.
  • Skapar beskrivning som visar information om bilden och den plats som 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

Miniatyrbildernas height och width används i både <img>-taggen och fälten h och w i miniatyrbildens webbadress. På så sätt kan Bing returnera en miniatyrbild med den exakta storleken.

Bestående klient-ID

Svar från API:er för Bing Search kan innehålla ett X-MSEdge-ClientID-huvud som ska skickas tillbaka till API:et med efterföljande förfrågningar. Om flera API:er för Bing-sökning används ska samma klient-ID användas för dem om möjligt.

När X-MSEdge-ClientID-huvudet tillhandahålls kan Bing-API:er associera alla sökningar för en användare, vilket är användbart i

Först hjälper Bing-sökmotorn till med att tillämpa tidigare kontexter på sökningarna för att hitta resultat som bättre tillfredsställer användaren. Om en användare tidigare har sökt efter termer som exempelvis relaterar till segling kan en senare sökning efter ”knopar” returnera information om knopar som används vid segling.

Därefter väljer Bing slumpmässigt ut användare som ska prova nya funktioner innan de blir allmänt tillgängliga. Genom att tillhandahålla samma klient-ID med varje begäran säkerställs att användare som har valts för att se en funktion alltid ser den. Utan klient-ID kan användaren se en funktion som sedan försvinner, till synes slumpmässigt, i sökresultatet.

Säkerhetsprinciper för webbläsaren (CORS) kan hindra att X-MSEdge-ClientID-huvudet visas för JavaScript. Den här begränsningen uppstår när söksvaret har ett annat ursprung än sidan som begärt det. I en produktionsmiljö bör du hantera den här principen genom att lägga upp ett serverskript som gör API-anrop på samma domän som webbsidan. Eftersom skriptet har samma ursprung som webbsidan är sedan X-MSEdge-ClientID-huvudet tillgängligt för JavaScript.

Anteckning

Du bör utföra begäran på serversidan i ett produktionsklart webbprogram ändå. I annat fall måste API-nyckeln för Bing-sökning inkluderas i 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 som görs av obehöriga personer, så det är viktigt att inte exponera nyckeln.

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 tillåta att självstudien får åtkomst till klientens ID-huvud. Installera Node.js om du inte redan har det. Sedan kör du följande kommando i ett kommandofönster:

npm install -g cors-proxy-server

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

Slutligen startar du CORS-proxyn med följande kommando:

cors-proxy-server

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

Nästa steg

Se även