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