Руководство по Создание одностраничного веб-приложения с помощью API Bing для поиска в ИнтернетеTutorial: Create a single-page app using the Bing Web Search API

Это одностраничное приложение показывает, как можно извлекать, анализировать и отображать актуальные результаты поиска из API Bing для поиска в Интернете.This single-page app demonstrates how to retrieve, parse, and display search results from the Bing Web Search API. fВ этом руководстве используется стандартный HTML и CSS и оно ориентировано на код JavaScript.The tutorial uses boilerplate HTML and CSS, and focuses on the JavaScript code. HTML, CSS и JS-файлы доступны на GitHub вместе с кратким руководством.HTML, CSS, and JS files are available on GitHub with quickstart instructions.

Этот пример приложения может выполнять такие задачи:This sample app can:

  • Вызов API Bing для поиска в Интернете с параметрами поиска.Call the Bing Web Search API with search options
  • Отображение результатов веб-поиска, а также результатов поиска новостей, видео и изображений.Display web, image, news, and video results
  • Разбивка результатов на страницы.Paginate results
  • Управление ключами подпискиManage subscription keys
  • Обработка ошибокHandle errors

Чтобы использовать это приложение, требуется учетная запись Azure Cognitive Services с API-интерфейсами поиска Bing.To use this app, an Azure Cognitive Services account with Bing Search APIs is required. Если у вас нет учетной записи, можно использовать бесплатную пробную версию, чтобы получить ключ подписки.If you don't have an account, you can use the free trial to get a subscription key.

Предварительные требованияPrerequisites

Для запуска приложения необходимы следующие элементы:Here are a few things that you'll need to run the app:

  • Node.js 8 или более поздняя версия.Node.js 8 or later
  • ключ подписки;A subscription key

Получение исходного кода и установка зависимостейGet the source code and install dependencies

Первым шагом является клонирование репозитория с исходным кодом примера приложения.The first step is to clone the repository with the sample app's source code.

git clone https://github.com/Azure-Samples/cognitive-services-REST-api-samples.git

Затем выполните npm install.Then run npm install. В этом руководстве единственной зависимостью является Express.js.For this tutorial, Express.js is the only dependency.

cd <path-to-repo>/cognitive-services-REST-api-samples/Tutorials/Bing-Web-Search
npm install

Компоненты приложенияApp components

Пример приложения, который мы создаем, состоит из четырех частей.The sample app we're building is made up of four parts:

  • bing-web-search.js — приложение Express.js.bing-web-search.js - Our Express.js app. Оно обрабатывает логику запроса и ответа и маршрутизацию.It handles request/response logic and routing.
  • public/index.html — структура приложения. Она определяет способ представления данных пользователю.public/index.html - The skeleton of our app; it defines how data is presented to the user.
  • public/css/styles.css — определяет стили страницы, такие как шрифты, цвет, размер текста.public/css/styles.css - Defines page styles, such as fonts, colors, text size.
  • public/js/scripts.js — содержит логику для выполнения запросов к API Bing для поиска в Интернете, управления ключами подписки, обработки и анализа ответов, а также логику отображения результатов.public/js/scripts.js - Contains the logic to make requests to the Bing Web Search API, manage subscription keys, handle and parse responses, and display results.

В этом руководстве рассматривается scripts.js и логика, необходимая для вызова API Bing для поиска в Интернете и обработки ответов.This tutorial focuses on scripts.js and the logic required to call the Bing Web Search API and handle the response.

HTML-формыHTML form

index.html включает форму, которая позволяет выполнять поиск и выбирать параметры поиска.The index.html includes a form that enables users to search and select search options. Атрибут onsubmit активируется при отправке формы, вызывая метод bingWebSearch(), определенный в scripts.js.The onsubmit attribute fires when the form is submitted, calling the bingWebSearch() method defined in scripts.js. Он принимает три аргумента:It takes three arguments:

  • Поисковый запросSearch query
  • Выбранные параметрыSelected options
  • Ключ подпискиSubscription key
<form name="bing" onsubmit="return bingWebSearch(this.query.value,
    bingSearchOptions(this), getSubscriptionKey())">

Параметры запросаQuery options

HTML-форма содержит параметры, которые соответствуют параметрам запросов в API Bing для поиска в Интернете версии 7.The HTML form includes options that map to query parameters in the Bing Web Search API v7. В этой таблице содержатся подробные сведения о том, как пользователи могут фильтровать результаты поиска с помощью примера приложения:This table provides a breakdown of how users can filter search results using the sample app:

ПараметрParameter ОПИСАНИЕDescription
query Текстовое поле для ввода строки запроса.A text field to enter a query string.
where Раскрывающееся меню для выбора рынка (язык и страна).A drop-down menu to select the market (location and language).
what Флажки для повышения уровня определенных типов результатов.Checkboxes to promote specific result types. Например, повышение уровня изображений увеличивает их ранжирование в результатах поиска.Promoting images, for example, increases the ranking of images in search results.
when Раскрывающееся меню, которое позволяет ограничить результаты поиска на сегодня, на этой неделе и в этом месяце.A drop-down menu that allows the user to limit the search results to today, this week, or this month.
safe Флажок для включения безопасного поиска Bing, который отфильтровывает материалы для взрослых.A checkbox to enable Bing SafeSearch, which filters out adult content.
count Скрытое поле.Hidden field. Количество результатов поиска для каждого запроса.The number of search results to return on each request. Измените значение, чтобы на странице отображалось меньше или больше результатов.Change this value to display fewer or more results per page.
offset Скрытое поле.Hidden field. Смещение первого результата поиска в запросе. Используется для разбиения по страницам.The offset of the first search result in the request, which is used for paging. Сбрасывается к 0 при каждом новом запросе.It's reset to 0 with each new request.

Примечание

API Bing для поиска в Интернете предоставляет дополнительные параметры запроса, чтобы уточнить результаты поиска.The Bing Web Search API offers additional query parameters to help refine search results. В этом примере используются всего несколько из них.This sample only uses a few. Полный список доступных параметров см. в этом разделе.For a complete list of available parameters, see Bing Web Search API v7 reference.

Функция bingSearchOptions() преобразует эти параметры, чтобы они соответствовали формату, требуемому API поиска Bing.The bingSearchOptions() function converts these options to match the format required by the Bing Search API.

// Build query options from selections in the HTML form.
function bingSearchOptions(form) {

    var options = [];
    // Where option.
    options.push("mkt=" + form.where.value);
    // SafeSearch option.
    options.push("SafeSearch=" + (form.safe.checked ? "strict" : "off"));
    // Freshness option.
    if (form.when.value.length) options.push("freshness=" + form.when.value);
    var what = [];
    for (var i = 0; i < form.what.length; i++)
        if (form.what[i].checked) what.push(form.what[i].value);
    // Promote option.
    if (what.length) {
        options.push("promote=" + what.join(","));
        options.push("answerCount=9");
    }
    // Count option.
    options.push("count=" + form.count.value);
    // Offset option.
    options.push("offset=" + form.offset.value);
    // Hardcoded text decoration option.
    options.push("textDecorations=true");
    // Hardcoded text format option.
    options.push("textFormat=HTML");
    return options.join("&");
}

Параметру SafeSearch можно присвоить значение strict, moderate или off. moderate — значение по умолчанию для службы "Поиск в Интернете Bing".SafeSearch can be set to strict, moderate, or off, with moderate being the default setting for Bing Web Search. В этой форме используется флажок, у которого есть два состояния.This form uses a checkbox, which has two states. В этом фрагменте кода SafeSearch присваивается значение strict или off, значение moderate не используется.In this snippet, SafeSearch is set to strict or off, moderate isn't used.

Если выбраны какие-либо флажки повышения уровня, то параметр answerCount также добавится к запросу.If any of the Promote checkboxes are selected, the answerCount parameter is added to the query. answerCount необходим при использовании параметра promote.answerCount is required when using the promote parameter. В этом фрагменте кода установлено значение 9 для возврата всех доступных типов результатов.In this snippet, the value is set to 9 to return all available result types.

Примечание

Повышение уровня типа результата не гарантирует, что он будет включен в результаты поиска.Promoting a result type doesn't guarantee that it will be included in the search results. Оно, скорее всего, повышает оценку этого типа результатов по сравнению со стандартной.Rather, promotion increases the ranking of those kinds of results relative to their usual ranking. Чтобы ограничить поиск конкретными видами результатов, используйте параметр запроса responseFilter или вызовите более конкретную конечную точку, например API Bing для поиска изображений или API Bing для поиска новостей.To limit searches to particular kinds of results, use the responseFilter query parameter, or call a more specific endpoint such as Bing Image Search or Bing News Search.

Параметры запроса textDecoration и textFormat жестко заданы в сценарии, чтобы поисковой запрос выделялся жирным шрифтом в результатах поиска.The textDecoration and textFormat query parameters are hardcoded into the script, and cause the search term to be boldfaced in the search results. Эти параметры необязательные.These parameters aren't required.

Управление ключами подпискиManage subscription keys

Чтобы не включать в код ключ подписки для API Поиска Bing, в этом примере приложения для его хранения используется постоянное хранилище браузера.To avoid hardcoding the Bing Search API subscription key, this sample app uses a browser's persistent storage to store the subscription key. Если ключ подписки не сохранен, пользователю предлагается ввести его.If no subscription key is stored, the user is prompted to enter one. Если он отклоняется API-интерфейсом, пользователю предлагается повторно ввести ключ подписки.If the subscription key is rejected by the API, the user is prompted to re-enter a subscription key.

Функция getSubscriptionKey() использует функции storeValue и retrieveValue для хранения и извлечения пользовательского ключа подписки.The getSubscriptionKey() function uses the storeValue and retrieveValue functions to store and retrieve a user's subscription key. Эти функции используют объект localStorage, если он поддерживается, или файлы cookie.These functions use the localStorage object, if supported, or cookies.

// Cookie names for stored data.
API_KEY_COOKIE   = "bing-search-api-key";
CLIENT_ID_COOKIE = "bing-search-client-id";

BING_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/search";

// See source code for storeValue and retrieveValue definitions.

// Get stored subscription key, or prompt if it isn't 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;
}

Как было показано ранее, при отправке формы активируется onsubmit, вызывая функцию bingWebSearch.As we saw earlier, when the form is submitted, onsubmit fires, calling bingWebSearch. Она инициализирует и отправляет запрос.This function initializes and sends the request. getSubscriptionKey вызывается для аутентификации запроса при каждой отправке.getSubscriptionKey is called on each submission to authenticate the request.

Учитывая запрос, строку параметров и ключ подписки, функция BingWebSearch создает объект XMLHttpRequest, чтобы вызвать конечную точку службы "Поиск в Интернете Bing".Given the query, the options string, and the subscription key, the BingWebSearch function creates an XMLHttpRequest object to call the Bing Web Search endpoint.

// Perform a search constructed from the query, options, and subscription key.
function bingWebSearch(query, options, key) {
    window.scrollTo(0, 0);
    if (!query.trim().length) return false;

    showDiv("noresults", "Working. Please wait.");
    hideDivs("pole", "mainline", "sidebar", "_json", "_http", "paging1", "paging2", "error");

    var request = new XMLHttpRequest();
    var queryurl = BING_ENDPOINT + "?q=" + encodeURIComponent(query) + "&" + options;

    // Initialize 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 errors.
    request.addEventListener("error", function() {
        renderErrorMessage("Error completing request");
    });

    // Event handler for an aborted request.
    request.addEventListener("abort", function() {
        renderErrorMessage("Request aborted");
    });

    // Send the request.
    request.send();
    return false;
}

После успешного запроса активируется обработчик событий load и вызывает функцию handleBingResponse.Following a successful request, the load event handler fires and calls the handleBingResponse function. handleBingResponse анализирует объект результата, отображает результаты и содержит логику ошибок для невыполненных запросов.handleBingResponse parses the result object, displays the results, and contains error logic for failed requests.

function handleBingResponse() {
    hideDivs("noresults");

    var json = this.responseText.trim();
    var jsobj = {};

    // Try to parse results object.
    try {
        if (json.length) jsobj = JSON.parse(json);
    } catch(e) {
        renderErrorMessage("Invalid JSON response");
        return;
    }

    // Show raw JSON and the 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 the HTTP response is 200 OK, try to render the 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 === "SearchResponse" && "rankingResponse" in jsobj) {
                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 considered an error.
    else {
        // 401 is unauthorized; force a re-prompt for the user's subscription
        // key on the next request.
        if (this.status === 401) invalidateSubscriptionKey();

        // Some error responses don't have a top-level errors object, if absent
        // create one.
        var errors = jsobj.errors || [jsobj];
        var errmsg = [];

        // Display the 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]);
        }

        // Display Bing Trace ID if it isn't blocked by CORS.
        var traceid = this.getResponseHeader("BingAPIs-TraceId");
        if (traceid) errmsg.push("\nTrace ID " + traceid);

        // Display the error message.
        renderErrorMessage(errmsg.join("\n"));
    }
}

Важно!

Успешный HTTP-запрос не означает, что сам поиск был успешным.A successful HTTP request doesn't mean that the search itself succeeded. Если во время выполнения операции поиска возникает ошибка, то API Bing для поиска в Интернете возвращает код состояния, отличный от "HTTP: 200", и включает сведения об ошибке в ответ JSON.If an error occurs in the search operation, the Bing Web Search API returns a non-200 HTTP status code and includes error information in the JSON response. Если у запроса было ограничение по скорости, то API возвращает пустой ответ.If the request was rate-limited, the API returns an empty response.

Большая часть кода в обеих описанных выше функциях предназначена для обработки ошибок.Much of the code in both of the preceding functions is dedicated to error handling. Ошибки могут возникать на следующих этапах:Errors may occur at the following stages:

ЭтапStage Возможные ошибкиPotential error(s) Чем обрабатываетсяHandled by
Создание объекта запросаBuilding the request object Недопустимый URL-адресInvalid URL Блок try / catchtry / catch block
Выполнение запросаMaking the request Ошибки сети, прерванные соединенияNetwork errors, aborted connections Обработчики событий error и aborterror and abort event handlers
Выполнение поискаPerforming the search Недопустимый запрос, недопустимый JSON-файл, ограничения скоростиInvalid request, invalid JSON, rate limits Тестируется в обработчике событий loadTests in load event handler

Ошибки обрабатываются путем вызова renderErrorMessage().Errors are handled by calling renderErrorMessage(). Если ответ проходит все проверки на ошибки, вызывается renderSearchResults() для отображения результатов поиска.If the response passes all of the error tests, renderSearchResults() is called to display the search results.

Отображение результатов поиска.Display search results

Существуют требования к использованию и отображению для результатов, возвращаемых API Bing для поиска в Интернете.There are use and display requirements for results returned by the Bing Web Search API. Так как ответ может содержать различные типы результатов, то недостаточно выполнить итерацию коллекции верхнего уровня WebPages.Since a response may include various result types, it isn't enough to iterate through the top-level WebPages collection. Вместо этого пример приложения использует RankingResponse для упорядочения результатов по спецификациям.Instead, the sample app uses RankingResponse to order the results to spec.

Примечание

Если требуется только один тип результата, используйте параметр запроса responseFilter или стоит воспользоваться другой конечной точкой Поиска Bing, например Поиском изображений Bing.If you only want a single result type, use the responseFilter query parameter, or consider using one of the other Bing Search endpoints, such as Bing Image Search.

Каждый ответ содержит объект RankingResponse, который может содержать до трех коллекций: pole, mainline и sidebar.Each response has a RankingResponse object that may include up to three collections: pole, mainline, and sidebar. pole (если есть) — это самый релевантный результат поиска, и он должен отображаться в приоритетном порядке.pole, if present, is the most relevant search result and must be prominently displayed. mainline содержит большую часть результатов поиска и отобразится сразу после pole.mainline contains most of the search results, and is displayed immediately after pole. sidebar включает вспомогательные результаты поиска.sidebar includes auxiliary search results. Если возможно, эти результаты должны отображаться на боковой панели.If possible, these results should be displayed in the sidebar. Если из-за ограничений экрана использование боковой панели становится нецелесообразным, эти результаты должны располагаться после результатов mainline.If screen limits make a sidebar impractical, these results should appear after the mainline results.

Каждый объект RankingResponse включает массив RankingItem, определяющий, как должны быть упорядочены результаты.Each RankingResponse includes a RankingItem array that specifies how results must be ordered. Наш пример приложения использует параметры answerType и resultIndex для идентификации результатов.Our sample app uses the answerType and resultIndex parameters to identify the result.

Примечание

Существуют дополнительные способы идентификации и отображения результатов.There are additional ways to identify and rank results. Дополнительные сведения см. в статье Использование ранжирования для отображения ответов.For more information, see Using ranking to display results.

Рассмотрим элементы кода:Let's take a look at the code:

// Render the search results from the JSON response.
function renderSearchResults(results) {

    // If spelling was corrected, update the search field.
    if (results.queryContext.alteredQuery)
        document.forms.bing.query.value = results.queryContext.alteredQuery;

    // Add Prev / Next links with result count.
    var pagingLinks = renderPagingLinks(results);
    showDiv("paging1", pagingLinks);
    showDiv("paging2", pagingLinks);

    // Render the results for each section.
    for (section in {pole: 0, mainline: 0, sidebar: 0}) {
        if (results.rankingResponse[section])
            showDiv(section, renderResultsItems(section, results));
    }
}

Функция renderResultsItems() выполняет перебор элементов в каждой коллекции RankingResponse, сопоставляет каждый результат ранжирования с результатом поиска с использованием значений answerType и resultIndex и вызывает соответствующую функцию рендеринга для создания HTML-кода.The renderResultsItems() function iterates through the items in each RankingResponse collection, maps each ranking result to a search result using the answerType and resultIndex values, and calls the appropriate rendering function to generate the HTML. Если resultIndex для данного элемента не указан, renderResultsItems() выполняет итерацию по всем результатам этого типа и вызывает функцию рендеринга для каждого элемента.If resultIndex isn't specified for an item, renderResultsItems() iterates through all results of that type and calls the rendering function for each item. Полученный HTML вставляется в соответствующий элемент <div> в index.html.The resulting HTML is inserted into the appropriate <div> element in index.html.

// Render search results from the RankingResponse object per rank response and
// use and display requirements.
function renderResultsItems(section, results) {

    var items = results.rankingResponse[section].items;
    var html = [];
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        // Collection name has lowercase first letter while answerType has uppercase
        // e.g. `WebPages` RankingResult type is in the `webPages` top-level collection.
        var type = item.answerType[0].toLowerCase() + item.answerType.slice(1);
        if (type in results && type in searchItemRenderers) {
            var render = searchItemRenderers[type];
            // This ranking item refers to ONE result of the specified type.
            if ("resultIndex" in item) {
                html.push(render(results[type].value[item.resultIndex], section));
            // This ranking item refers to ALL results of the specified type.
            } else {
                var len = results[type].value.length;
                for (var j = 0; j < len; j++) {
                    html.push(render(results[type].value[j], section, j, len));
                }
            }
        }
    }
    return html.join("\n\n");
}

Просмотр функций отрисовщикаReview renderer functions

В нашем примере объект searchItemRenderers включает функции, которые создают HTML-код для каждого типа результатов поиска.In our sample app, the searchItemRenderers object includes functions that generate HTML for each type of search result.

// Render functions for each result type.
searchItemRenderers = {
    webPages: function(item) { ... },
    news: function(item) { ... },
    images: function(item, section, index, count) { ... },
    videos: function(item, section, index, count) { ... },
    relatedSearches: function(item, section, index, count) { ... }
}

Важно!

В примере приложения есть отрисовщики для веб-страниц, новостей, изображений, видео и связанных с ними поисковых запросов.The sample app has renderers for web pages, news, images, videos, and related searches. Вашему приложению нужны отрисовщики для любого вида полученных результатов, которые могут включать вычисления, предложения орфографии, сущности, часовые пояса и определения.Your application will need renderers for any type of results it may receive, which could include computations, spelling suggestions, entities, time zones, and definitions.

Некоторые функции рендеринга принимают только параметр item.Some of the rendering functions accept only the item parameter. Другие принимают дополнительные параметры, которые могут использоваться для преобразования элементов для просмотра по-разному в разных контекстах.Others accept additional parameters, which can be used to render items differently based on context. Отрисовщик, который не использует эту информацию, не должен принимать эти параметры.A renderer that doesn't use this information doesn't need to accept these parameters.

Контекстные аргументы:The context arguments are:

ПараметрParameter ОПИСАНИЕDescription
section Раздел результатов (pole, mainline или sidebar), в котором содержится элемент.The results section (pole, mainline, or sidebar) in which the item appears.
index
count
Доступен, когда элемент RankingResponse указывает, что все результаты в данной коллекции должны отображаться. В противном случае undefined.Available when the RankingResponse item specifies that all results in a given collection are to be displayed; undefined otherwise. Это индекс элемента и общее число элементов коллекции.The index of the item within its collection and the total number of items in that collection. Вы можете использовать эту информацию для подсчета результатов, для создания разного HTML-кода для первого или последнего результата и т. д.You can use this information to number the results, to generate different HTML for the first or last result, and so on.

В нашем примере приложения отрисовщики images и relatedSearches используют контекстные аргументы для настройки создаваемого HTML-кода.In the sample app, both the images and relatedSearches renderers use the context arguments to customize the generated HTML. Давайте рассмотрим отрисовщик images подробнее:Let's take a closer look at the images renderer:

searchItemRenderers = {
    // Render image result with thumbnail.
    images: function(item, section, index, count) {
        var height = 60;
        var width = Math.round(height * item.thumbnail.width / item.thumbnail.height);
        var html = [];
        if (section === "sidebar") {
            if (index) html.push("<br>");
        } else {
            if (!index) html.push("<p class='images'>");
        }
        html.push("<a href='" + item.hostPageUrl + "'>");
        var title = escape(item.name) + "\n" + getHost(item.hostPageDisplayUrl);
        html.push("<img src='"+ item.thumbnailUrl + "&h=" + height + "&w=" + width +
            "' height=" + height + " width=" + width + " title='" + title + "' alt='" + title + "'>");
        html.push("</a>");
        return html.join("");
    },
    // Other renderers are omitted from this sample...
}

Отрисовщик изображений:The image renderer:

  • Вычисляет размер эскиза изображения (ширина изменяется, а высота фиксируется на 60 пикселях).Calculates the image thumbnail size (width varies, while height is fixed at 60 pixels).
  • Вставляет HTML-код, предшествующий результату изображения на основе контекста.Inserts the HTML that precedes the image result based on context.
  • Создает HTML-тег <a>, который связывает со страницей, содержащей это изображение.Builds the HTML <a> tag that links to the page that contains the image.
  • создает HTML-тег <img> для отображения эскиза изображения;Builds the HTML <img> tag to display the image thumbnail.

Отрисовщик изображений использует переменные section и index, чтобы отобразить результаты по-разному, в зависимости от того, где они отображаются.The image renderer uses the section and index variables to display results differently depending on where they appear. Разрыв строки (тег <br>) вставляется между результатами изображения на боковой панели, чтобы в боковой панели изображения отображались в столбце.A line break (<br> tag) is inserted between image results in the sidebar, so that the sidebar displays a column of images. В других разделах первый результат изображения (index === 0) предшествует тегу <p>.In other sections, the first image result (index === 0) is preceded by a <p> tag.

Размер эскиза указывается в теге <img> и в полях h и w URL-адреса эскиза.The thumbnail size is used in both the <img> tag and the h and w fields in the thumbnail's URL. Атрибуты title и alt (текстовое описание изображения) формируются на основе имени изображения и имени узла в URL-адресе.The title and alt attributes (a textual description of the image) are constructed from the image's name and the hostname in the URL.

Ниже приведен пример отображения изображений в примере приложения:Here's an example of how images are displayed in the sample app:

[Результаты изображения Bing]

Сохранение идентификатора клиентаPersist the client ID

Ответы от интерфейсов API для поиска Bing могут содержать заголовок X-MSEdge-ClientID, который необходимо отправить обратно в API с каждым последующим запросом.Responses from the Bing search APIs may include a X-MSEdge-ClientID header that should be sent back to the API with each successive request. Если приложение использует несколько API для поиска Bing, убедитесь, что с каждым запросом в службах отправляется одинаковый идентификатор клиента.If more than one of the Bing Search APIs is used by your app, make sure the same client ID is sent with each request across services.

Наличие заголовка X-MSEdge-ClientID позволяет API-интерфейсам Bing связывать поисковые запросы пользователя.Providing the X-MSEdge-ClientID header allows the Bing APIs to associate a user's searches. Во-первых, поисковая система Bing может применять к операциям поиска прошлый контекст и находить результаты, которые лучше соответствуют требованиям запроса.First, it allows the Bing search engine to apply past context to searches to find results that better satisfy the request. Если пользователь ранее уже вводил поисковые запросы, например, связанные с мореплаванием под парусом, то при последующем поиске по слову "морские узлы" в приоритетном порядке могут возвращаться сведения об узлах, которые используются в мореплавании.If a user has previously searched for terms related to sailing, for example, a later search for "knots" might preferentially return information about knots used in sailing. Во-вторых, Bing может случайным образом выбирать пользователей для опробования новых возможностей, прежде чем делать их общедоступными.Second, Bing may randomly select users to experience new features before they are made widely available. Предоставление одного и того же идентификатора клиента при каждом запросе гарантирует, что пользователи, которым был открыт доступ к определенному компоненту, всегда будут его видеть.Providing the same client ID with each request ensures that users who have been chosen to see a feature will always see it. Без идентификатора клиента такой компонент может появляться и исчезать в результатах поиска пользователя как бы случайным образом.Without the client ID, the user might see a feature appear and disappear, seemingly at random, in their search results.

Политики безопасности браузера, такие как "Общий доступ к ресурсам независимо от источника" (CORS), могут запретить доступ примеру приложения к заголовку X-MSEdge-ClientID.Browser security policies, such as Cross-Origin Resource Sharing (CORS), may prevent the sample app from accessing the X-MSEdge-ClientID header. Это ограничение возникает, когда ответ поиска и страница, его запросившая, имеют разные источники.This limitation occurs when the search response has a different origin from the page that requested it. В рабочей среде такая проблема с политикой решается путем размещения серверного скрипта, который выполняет вызов API, на одном домене с веб-страницей.In a production environment, you should address this policy by hosting a server-side script that does the API call on the same domain as the Web page. Так как скрипт будет иметь тот же источник, что и веб-страница, заголовок X-MSEdge-ClientID станет доступным для JavaScript.Since the script has the same origin as the Web page, the X-MSEdge-ClientID header is then available to JavaScript.

Примечание

В рабочем веб-приложении запрос следует всегда выполнять на стороне сервера.In a production Web application, you should perform the request server-side anyway. В противном случае вам придется включать в веб-страницу ключ подписки API для поиска Bing, где он будет доступен для всех, кто просматривает исходный код.Otherwise, your Bing Search API subscription key must be included in the web page, where it's available to anyone who views source. С вас будет взиматься плата за любое использование ресурсов в рамках вашего ключа подписки API, включая запросы, выполненные неавторизованными сторонами, поэтому важно не предоставлять доступ к своему ключу.You are billed for all usage under your API subscription key, even requests made by unauthorized parties, so it is important not to expose your key.

В целях разработки запрос можно выполнить через прокси-сервер CORS.For development purposes, you can make a request through a CORS proxy. Ответ от этого типа прокси-сервера содержит заголовок Access-Control-Expose-Headers, который вносит заголовки ответов в список разрешений и делает их доступными для JavaScript.The response from this type of proxy has an Access-Control-Expose-Headers header that whitelists response headers and makes them available to JavaScript.

Установить прокси-сервер CORS довольно просто. Это позволит нашему примеру приложения получить доступ к заголовку идентификатора клиента.It's easy to install a CORS proxy to allow our sample app to access the client ID header. Выполните следующую команду:Run this command:

npm install -g cors-proxy-server

После этого измените конечную точку службы "Поиск в Интернете Bing" в script.js на следующую:Next, change the Bing Web Search endpoint in script.js to:

http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search

Запустите прокси-сервер CORS с помощью следующей команды:Start the CORS proxy with this command:

cors-proxy-server

Не закрывайте командное окно, пока используете пример приложения. Если закрыть окно, это приведет к остановке прокси-сервера.Leave the command window open while you use the sample app; closing the window stops the proxy. В развертываемом разделе заголовков HTTP под результатами поиска можно увидеть заголовок X-MSEdge-ClientID.In the expandable HTTP Headers section below the search results, the X-MSEdge-ClientID header should be visible. Убедитесь, что он одинаковый для всех запросов.Verify that it's the same for each request.

Дополнительная информацияNext steps