Tutorial: Aplicación web de una sola páginaTutorial: Single-page web app

Bing Entity Search API le permite buscar en Internet información acerca de entidades y lugares.The Bing Entity Search API lets you search the Web for information about entities and places. Puede solicitar cualquier tipo de resultado, o ambos, en una consulta determinada.You may request either kind of result, or both, in a given query. Las definiciones de lugares y entidades se proporcionan a continuación.The definitions of places and entities are provided below.

EntidadesEntities Gente conocida, lugares y cosas que puede encontrar en función del nombre.Well-known people, places, and things that you find by name
LugaresPlaces Restaurantes, hoteles y otras empresas locales que encuentra en función del nombre o el tipo (restaurantes italianos).Restaurants, hotels, and other local businesses that you find by name or by type (Italian restaurants)

En este tutorial, creamos una aplicación web de una sola página que usa Bing Entity Search API para mostrar los resultados de la búsqueda directamente en la página.In this tutorial, we build a single-page Web application that uses the Bing Entity Search API to display search results right in the page. La aplicación incluye componentes HTML, CSS y JavaScript.The application includes HTML, CSS, and JavaScript components.

Esta API le permite priorizar los resultados según la ubicación.The API lets you prioritize results by location. En una aplicación móvil, puede solicitar al dispositivo su propia ubicación.In a mobile app, you can ask the device for its own location. En cambio, en una aplicación web, puede usar la función getPosition().In a Web app, you can use the getPosition() function. Pero esta llamada solo funciona en contextos seguros y es posible que no proporcione una ubicación precisa.But this call works only in secure contexts, and it may not provide a precise location. Además, es posible que el usuario quiera buscar entidades cerca de una ubicación que no sea la suya.Also, the user may want to search for entities near a location other than their own.

Por lo tanto, nuestra aplicación recurre al servicio Mapas de Bing para obtener la latitud y la longitud desde una ubicación que haya especificado el usuario.Our app therefore calls upon the Bing Maps service to obtain latitude and longitude from a user-entered location. El usuario solo tiene que escribir el nombre de un punto de referencia ("Space Needle") o una dirección completa o parcial ("New York City"), y la API de Mapas de Bing le proporciona las coordenadas.The user can then enter the name of a landmark ("Space Needle") or a full or partial address ("New York City"), and the Bing Maps API provides the coordinates.

Nota

Los encabezados JSON y HTTP de la parte inferior de la página revelan la respuesta JSON y la información de solicitud HTTP cuando se hace clic.The JSON and HTTP headings at the bottom of the page reveal the JSON response and HTTP request information when clicked. Estos detalles resultan útiles al explorar el servicio.These details are useful when exploring the service.

En la aplicación del tutorial se muestra cómo:The tutorial app illustrates how to:

  • Realizar una llamada a Bing Entity Search API en JavaScriptPerform a Bing Entity Search API call in JavaScript
  • Realizar una llamada a la API locationQuery de Mapas de Bing en JavaScriptPerform a Bing Maps locationQuery API call in JavaScript
  • Pasar opciones de búsqueda a las llamadas a APIPass search options to the API calls
  • Mostrar los resultados de la búsquedaDisplay search results
  • Administrar el id. de cliente de Bing y las claves de suscripción de la APIHandle the Bing client ID and API subscription keys
  • Controlar los errores que se puedan producirDeal with any errors that might occur

La página del tutorial es completamente independiente: no utiliza marcos, hojas de estilo ni archivos de imagen externos.The tutorial page is entirely self-contained; it does not use any external frameworks, style sheets, or even image files. Usa solo características del lenguaje JavaScript ampliamente compatibles y funciona con las versiones actuales de los principales exploradores web.It uses only widely supported JavaScript language features and works with current versions of all major Web browsers.

En este tutorial, solo se describen determinadas partes del código fuente.In this tutorial, we discuss only selected portions of the source code. El código fuente completo está disponible en otra página.The full source code is available on a separate page. Copie y pegue este código en un editor de texto y guárdelo como bing.html.Copy and paste this code into a text editor and save it as bing.html.

Nota

Este tutorial es bastante similar al tutorial de la aplicación Bing Web Search de una sola página, pero solo se centra en los resultados de búsqueda de imágenes.This tutorial is substantially similar to the single-page Bing Web Search app tutorial, but deals only with entity search results.

Componentes de la aplicaciónApp components

Al igual que cualquier aplicación web de una sola página, la aplicación del tutorial incluye tres partes:Like any single-page Web app, the tutorial application includes three parts:

  • HTML: define la estructura y el contenido de la páginaHTML - Defines the structure and content of the page
  • CSS: define el aspecto de la páginaCSS - Defines the appearance of the page
  • JavaScript: define el comportamiento de la páginaJavaScript - Defines the behavior of the page

Este tutorial no profundiza en la mayor parte de HTML o CSS, ya que es sencillo.This tutorial doesn't cover most of the HTML or CSS in detail, as they are straightforward.

El código HTML contiene el formulario de búsqueda en que el usuario escribe una consulta y elige las opciones de búsqueda.The HTML contains the search form in which the user enters a query and chooses search options. El formulario está conectado al código JavaScript que realmente realiza la búsqueda mediante el atributo onsubmit de la etiqueta <form>:The form is connected to the JavaScript that actually performs the search by the <form> tag's onsubmit attribute:

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

El controlador onsubmit devuelve false, lo que impide que el formulario que se envíe a un servidor.The onsubmit handler returns false, which keeps the form from being submitted to a server. El código JavaScript hace el trabajo de recopilar la información necesaria del formulario y realizar la búsqueda.The JavaScript code actually does the work of collecting the necessary information from the form and performing the search.

La búsqueda se realiza en dos fases.The search is done in two phases. En primer lugar, si el usuario ha escrito una restricción de ubicación, se realiza una consulta de Mapas de Bing para convertirla en coordenadas.First, if the user has entered a location restriction, a Bing Maps query is done to convert it into coordinates. A continuación, la devolución de llamada de esta consulta inicia la consulta de Bing Entity Search.The callback for this query then kicks off the Bing Entity Search query.

El código HTML también contiene las divisiones (etiquetas <div> HTML) donde aparecen los resultados de búsqueda.The HTML also contains the divisions (HTML <div> tags) where the search results appear.

Administrar la clave de suscripciónManaging subscription keys

Nota

Esta aplicación requiere claves de suscripción para Bing Search API y la API de Mapas de Bing.This app requires subscription keys for both the Bing Search API and the Bing Maps API. Puede utilizar una clave de prueba de Bing Search y una clave básica de Mapas de Bing.You can use a trial Bing Search key and a basic Bing Maps key.

Para evitar tener que incluir las claves de suscripción de Bing Search API y la API de Mapas de Bing en el código, usamos el almacenamiento persistente del explorador para almacenar las claves.To avoid having to include the Bing Search and Bing Maps API subscription keys in the code, we use the browser's persistent storage to store them. Si no se ha almacenado ninguna de las dos claves, las solicitamos y las almacenamos para su uso posterior.If either key has not been stored, we prompt for it and store it for later use. Si, más tarde, la API rechaza la clave, se invalidará la clave almacenada y se le volverá a preguntar al usuario en la siguiente búsqueda.If the key is later rejected by the API, we invalidate the stored key so the user is asked for it upon their next search.

Definimos funciones storeValue y retrieveValue que usan el objeto localStorage (si el explorador lo admite) o una cookie.We define storeValue and retrieveValue functions that use either the localStorage object (if the browser supports it) or a cookie. Nuestra función getSubscriptionKey() usa estas funciones para almacenar y recuperar la clave del usuario.Our getSubscriptionKey() function uses these functions to store and retrieve the user's key.

// cookie names for data we store
SEARCH_API_KEY_COOKIE = "bing-search-api-key";
MAPS_API_KEY_COOKIE   = "bing-maps-api-key";
CLIENT_ID_COOKIE      = "bing-search-client-id";

// API endpoints
SEARCH_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/entities";
MAPS_ENDPOINT   = "https://dev.virtualearth.net/REST/v1/Locations";

// ... omitted definitions of storeValue() and retrieveValue()

// get stored API subscription key, or prompt if it's not found
function getSubscriptionKey(cookie_name, key_length, api_name) {
    var key = retrieveValue(cookie_name);
    while (key.length !== key_length) {
        key = prompt("Enter " + api_name + " API subscription key:", "").trim();
    }
    // always set the cookie in order to update the expiration date
    storeValue(cookie_name, key);
    return key;
}

function getMapsSubscriptionKey() {
    return getSubscriptionKey(MAPS_API_KEY_COOKIE, 64, "Bing Maps");
}

function getSearchSubscriptionKey() {
    return getSubscriptionKey(SEARCH_API_KEY_COOKIE, 32, "Bing Search");
}

La etiqueta HTML <body> incluye un atributo onload que llama a getSearchSubscriptionKey() y getMapsSubscriptionKey() cuando la página ha terminado de cargarse.The HTML <body> tag includes an onload attribute that calls getSearchSubscriptionKey() and getMapsSubscriptionKey() when the page has finished loading. Estas llamadas sirven para solicitar inmediatamente al usuario sus claves, si es que aún no las ha introducido.These calls serve to immediately prompt the user for their keys if they haven't yet entered them.

<body onload="document.forms.bing.query.focus(); getSearchSubscriptionKey(); getMapsSubscriptionKey();">

Seleccionar opciones de búsquedaSelecting search options

[impreso de Bing Entity Search]

El formulario HTML incluye los siguientes controles:The HTML form includes the following controls:

where Un menú desplegable para seleccionar el mercado (ubicación e idioma) que se usa en la búsqueda.A drop-down menu for selecting the market (location and language) used for the search.
query El campo de texto en el que se especificarán los términos de búsqueda.The text field in which to enter the search terms.
safe Una casilla que indica si SafeSearch está activado (restringe los resultados para "adultos").A checkbox indicating whether SafeSearch is turned on (restricts "adult" results)
what Un menú para elegir buscar entidades, lugares o ambos.A menu for choosing to search for entities, places, or both.
mapquery El campo de texto en el que el usuario puede escribir una dirección completa o parcial, un punto de referencia, etc. para que Bing Entity Search pueda devolver resultados más relevantes.The text field in which the user may enter a full or partial address, a landmark, etc. to help Bing Entity Search return more relevant results.

Nota

Los resultados de la opción Lugares solo están actualmente disponibles en los Estados Unidos.Places results are currently available only in the United States. Los menús where y what tienen código para aplicar esta restricción.The where and what menus have code to enforce this restriction. Si elige un mercado fuera de los EE. UU. al seleccionar Lugares en el menú what, what cambia a Cualquier cosa.If you choose a non-US market while Places is selected in the what menu, what changes to Anything. Si elige Lugares y selecciona un mercado fuera de los EE. UU. En el menú where, where cambia a los EE. UU.If you choose Places while a non-US market is selected in the where menu, where changes to the US.

Nuestra función de JavaScript bingSearchOptions() convierte estos campos en una cadena de consulta parcial en para Bing Search API.Our JavaScript function bingSearchOptions() converts these fields to a partial query string for the Bing Search API.

// 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.what.selectedIndex) options.push("responseFilter=" + form.what.value);
    return options.join("&");
}

Por ejemplo, la característica Búsqueda segura puede tener el valor strict, moderate u off. El valor predeterminado es moderate.For example, the SafeSearch feature can be strict, moderate, or off, with moderate being the default. Sin embargo, nuestro formulario usa una casilla que solo tiene dos estados.But our form uses a checkbox, which has only two states. El código JavaScript convierte este valor en strict u off (no usamos moderate).The JavaScript code converts this setting to either strict or off (we don't use moderate).

El campo mapquery no se maneja en bingSearchOptions() porque se usa en la consulta de ubicación de Mapas de Bing, no para Bing Entity Search.The mapquery field isn't handled in bingSearchOptions() because it is used for the Bing Maps location query, not for Bing Entity Search.

Obtener una ubicaciónObtaining a location

La API de Mapas de Bing ofrece un locationQuerymétodo, que usamos para encontrar la latitud y la longitud de la ubicación que especifica el usuario.The Bing Maps API offers a locationQuery method, which we use to find the latitude and longitude of the location the user enters. Estas coordenadas se pasan a Bing Entity Search API junto con la solicitud del usuario.These coordinates are then passed to the Bing Entity Search API with the user's request. Los resultados de búsqueda clasifican las entidades y los lugares que están cerca de la ubicación especificada.The search results prioritize entities and places that are close to the specified location.

No podemos acceder a la API de Mapas de Bing si usamos una consulta ordinaria XMLHttpRequest en una aplicación web, porque el servicio no admite consultas de origen cruzado.We can't access the Bing Maps API using an ordinary XMLHttpRequest query in a Web app because the service does not support cross-origin queries. Afortunadamente, es compatible con JSONP (la "P" se refiere a "padded" o acolchado).Fortunately, it supports JSONP (the "P" is for "padded"). Una respuesta JSONP es una respuesta JSON ordinaria encapsulada en una llamada a la función.A JSONP response is an ordinary JSON response wrapped in a function call. La solicitud se realiza insertándola con una etiqueta <script> en el documento.The request is made by inserting using a <script> tag into the document. (La carga de scripts no está sujeta a las directivas de seguridad del explorador).(Loading scripts is not subject to browser security policies.)

La función bingMapsLocate() crea e inserta la etiqueta <script> para la consulta.The bingMapsLocate() function creates and inserts the <script> tag for the query. El segmento jsonp=bingMapsCallback de la cadena de consulta especifica el nombre de la función que se llamará con la respuesta.The jsonp=bingMapsCallback segment of the query string specifies the name of the function to be called with the response.

function bingMapsLocate(where) {

    where = where.trim();
    var url = MAPS_ENDPOINT + "?q=" + encodeURIComponent(where) + 
                "&jsonp=bingMapsCallback&maxResults=1&key=" + getMapsSubscriptionKey();

    var script = document.getElementById("bingMapsResult")
    if (script) script.parentElement.removeChild(script);

    // global variable holds reference to timer that will complete the search if the maps query fails
    timer = setTimeout(function() {
        timer = null;
        var form = document.forms.bing;
        bingEntitySearch(form.query.value, "", bingSearchOptions(form), getSearchSubscriptionKey());
    }, 5000);

    script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("id", "bingMapsResult");
    script.setAttribute("src", url);
    script.setAttribute("onerror", "BingMapsCallback(null)");
    document.body.appendChild(script);

    return false;
}

Nota

Si la API de Mapas de Bing no responde, nunca se llama a la función bingMapsCallBack().If the Bing Maps API does not respond, the bingMapsCallBack() function is never called. Esto normalmente significa que no se llama a bingEntitySearch(), y que no aparecen los resultados de la búsqueda de la entidad.Ordinarily, that would mean that bingEntitySearch() isn't called, and the entity search results do not appear. Para evitar esta situación, bingMapsLocate() también establece un temporizador para llamar a bingEntitySearch() después de cinco segundos.To avoid this scenario, bingMapsLocate() also sets a timer to call bingEntitySearch() after five seconds. Hay lógica en la función de devolución de llamada para evitar realizar la búsqueda de entidades dos veces.There is logic in the callback function to avoid performing the entity search twice.

Cuando la consulta finaliza, se llama a la función bingMapsCallback() según lo solicitado.When the query completes, the bingMapsCallback() function is called, as requested.

function bingMapsCallback(response) {

    if (timer) {    // we beat the timer; stop it from firing
        clearTimeout(timer);
        timer = null;
    } else {        // the timer beat us; don't do anything
        return; 
    }

    var location = "";
    var name = "";
    var radius = 1000;

    if (response) {
        try {
            if (response.statusCode === 401) {
                invalidateMapsKey();
            } else if (response.statusCode === 200) {
                var resource = response.resourceSets[0].resources[0];
                var coords   = resource.point.coordinates;
                name         = resource.name;

                // the radius is the largest of the distances between the location and the corners
                // of its bounding box (in case it's not in the center) with a minimum of 1 km
                try {
                    var bbox    = resource.bbox;
                    radius  = Math.max(haversineDistance(bbox[0], bbox[1], coords[0], coords[1]),
                                       haversineDistance(coords[0], coords[1], bbox[2], bbox[1]),
                                       haversineDistance(bbox[0], bbox[3], coords[0], coords[1]),
                                       haversineDistance(coords[0], coords[1], bbox[2], bbox[3]), 1000);
                } catch(e) {  }
                var location = "lat:" + coords[0] + ";long:" + coords[1] + ";re:" + Math.round(radius);
            }
        }
        catch (e) { }   // response is unexpected. this isn't fatal, so just don't provide location
    }

    var form = document.forms.bing;
    if (name) form.mapquery.value = name;
    bingEntitySearch(form.query.value, location, bingSearchOptions(form), getSearchSubscriptionKey());

}

Además de la latitud y la longitud, la consulta de Bing Entity Search requiere un radio que indique la precisión de la información de la ubicación.Along with latitude and longitude, the Bing Entity Search query requires a radius that indicates the precision of the location information. Calculamos el radio utilizando el rectángulo delimitador provisto en la respuesta de Mapas de Bing.We calculate the radius using the bounding box provided in the Bing Maps response. El rectángulo delimitador es un rectángulo que rodea toda la ubicación.The bounding box is a rectangle that surrounds the entire location. Por ejemplo, si el usuario escribe NYC, el resultado contiene aproximadamente las coordenadas centrales de la ciudad de Nueva York y un rectángulo delimitador que abarca la ciudad.For example, if the user enters NYC, the result contains roughly central coordinates of New York City and a bounding box that encompasses the city.

Primero calculamos las distancias desde las coordenadas principales hacia cada una de las cuatro esquinas del rectángulo delimitador, mediante la función haversineDistance() (no se muestra).We first calculate the distances from the primary coordinates to each of the four corners of the bounding box using the function haversineDistance() (not shown). Usamos la mayor de estas cuatro distancias a modo de radio.We use the largest of these four distances as the radius. El radio mínimo es de un kilómetro.The minimum radius is a kilometer. Este valor también se usa como valor predeterminado si no se proporciona un rectángulo delimitador en la respuesta.This value is also used as a default if no bounding box is provided in the response.

Una vez obtenidos los valores de las coordenadas y el radio, llamamos a bingEntitySearch() para realizar la búsqueda real.Having obtained the coordinates and the radius, we then call bingEntitySearch() to perform the actual search.

Dada la consulta, una ubicación, una cadena de opciones y la clave de API, la función BingEntitySearch() realiza la solicitud de búsqueda de Bing Entity Search.Given the query, a location, an options string, and the API key, the BingEntitySearch() function makes the Bing Entity Search request.

// perform a search given query, location, options string, and API keys
function bingEntitySearch(query, latlong, 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("pole", "mainline", "sidebar", "_json", "_http", "error");

    var request = new XMLHttpRequest();
    var queryurl = SEARCH_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);

    if (latlong) request.setRequestHeader("X-Search-Location", latlong);

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

Tras la correcta finalización de la solicitud HTTP, JavaScript llama a nuestro controlador de eventos load, la función handleBingResponse(), para controlar una solicitud GET HTTP correcta a la API.Upon successful completion of the HTTP request, JavaScript calls our load event handler, the handleBingResponse() function, to handle a successful HTTP GET request to the API.

// 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 === "SearchResponse") {
                renderSearchResults(jsobj);
            } else {
                renderErrorMessage("No search results in JSON response");
            }
        } else {
            renderErrorMessage("Empty response (are you sending too many requests too quickly?)");
        }
    if (divHidden("pole") && divHidden("mainline") && divHidden("sidebar")) 
        showDiv("noresults", "No results.<p><small>Looking for restaurants or other local businesses? Those currently areen't supported outside the US.</small>");
    }

    // Any other HTTP status is an error
    else {
        // 401 is unauthorized; force re-prompt for API key for next request
        if (this.status === 401) invalidateSearchKey();

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

Una solicitud HTTP correcta no indica necesariamente que la búsqueda se haya realizado correctamente.A successful HTTP request does not necessarily mean that the search itself succeeded. Si se produce un error en la operación de búsqueda, Bing Entity Search API devuelve un código de estado HTTP distinto de 200, e incluye información de error en la respuesta JSON.If an error occurs in the search operation, the Bing Entity Search API returns a non-200 HTTP status code and includes error information in the JSON response. Además, si la solicitud tenía limitación de frecuencia, la API devuelve una respuesta vacía.Additionally, if the request was rate-limited, the API returns an empty response.

Gran parte del código de las dos funciones anteriores está dedicado al control de errores.Much of the code in both of the preceding functions is dedicated to error handling. Pueden producirse errores en las siguientes fases:Errors may occur at the following stages:

FaseStage Errores posiblesPotential error(s) Controlado porHandled by
Crear un objeto de solicitud de JavaScriptBuilding JavaScript request object Dirección URL no válidaInvalid URL Bloqueo try/catchtry/catch block
Hacer la solicitudMaking the request Errores de red, conexiones anuladasNetwork errors, aborted connections Controladores de eventos error y aborterror and abort event handlers
Realizar la búsquedaPerforming the search Solicitud no válida, JSON no válido, límites de frecuenciaInvalid request, invalid JSON, rate limits Pruebas en el controlador de eventos loadtests in load event handler

Los errores se controlan mediante una llamada a renderErrorMessage() con los detalles que se conocen sobre el error.Errors are handled by calling renderErrorMessage() with any details known about the error. Si la respuesta pasa todas las pruebas de error, llamamos a renderSearchResults() para mostrar los resultados de la búsqueda en la página.If the response passes the full gauntlet of error tests, we call renderSearchResults() to display the search results in the page.

Mostrar los resultados de búsquedaDisplaying search results

Bing Entity Search API le solicitará mostrar los resultados en un orden específico.The Bing Entity Search API requires you to display results in a specified order. Dado que la API puede devolver dos tipos diferentes de respuestas, no es suficiente iterar a través de la colección de nivel superior Entities o Places en la respuesta JSON y mostrar esos resultados.Since the API may return two different kinds of responses, it is not enough to iterate through the top-level Entities or Places collection in the JSON response and display those results. (Si solo quiere un tipo de resultado, use el parámetro de consulta responseFilter).(If you want only one type of result, use the responseFilter query parameter.)

En su lugar, usamos la colección rankingResponse en los resultados de la búsqueda para ordenar los resultados que se mostrarán.Instead, we use the rankingResponse collection in the search results to order the results for display. Este objeto se refiere a los artículos en las colecciones Entitiess o Places.This object refers to items in the Entitiess and/or Places collections.

rankingResponse puede contener hasta tres colecciones designadas de resultados de búsqueda; esto es, pole, mainline y sidebar.rankingResponse may contain up to three collections of search results, designated pole, mainline, and sidebar.

pole, si está presente, es el resultado de búsqueda más apropiado y debe mostrarse de manera destacada.pole, if present, is the most relevant search result and should be displayed prominently. mainline se refiere a la mayor parte de los resultados de la búsqueda.mainline refers to the bulk of the search results. Los resultados principales se deben mostrar inmediatamente después de pole (o antes, si pole no está presente).Mainline results should be displayed immediately after pole (or first, if pole is not present).

Por último.Finally. sidebar hace referencia a los resultados de búsqueda auxiliares.sidebar refers to auxiliary search results. Se pueden mostrar en una barra lateral o simplemente después de los resultados principales.They may be displayed in an actual sidebar or simply after the mainline results. Hemos elegido este último para nuestra aplicación tutorial.We have chosen the latter for our tutorial app.

Cada elemento en una colección rankingResponse hace referencia a los resultados reales de la búsqueda, y en dos formas diferentes pero equivalentes.Each item in a rankingResponse collection refers to the actual search result items in two different, but equivalent, ways.

id id parece una dirección URL, pero no debe usarse para los vínculos.The id looks like a URL, but should not be used for links. El tipo id de un resultado de clasificación coincide con el id de un elemento del resultado de búsqueda en una colección de respuestas, o una colección de respuestas completa (como, por ejemplo, Entities).The id type of a ranking result matches the id of either a search result item in an answer collection, or an entire answer collection (such as Entities).
answerType
resultIndex
answerType se refiere a la colección de respuestas de nivel superior que contiene el resultado (por ejemplo, Entities).The answerType refers to the top-level answer collection that contains the result (for example, Entities). resultIndex se refiere al índice del resultado dentro de la colección.The resultIndex refers to the result's index within that collection. Si se omite resultIndex, el resultado de la clasificación se refiere a toda la colección.If resultIndex is omitted, the ranking result refers to the entire collection.

Nota

Para más información acerca de esta parte de la respuesta de búsqueda, consulte Clasificación de resultados.For more information on this part of the search response, see Rank Results.

Puede usar el método que sea más conveniente para su aplicación para ubicar el elemento resultado de la búsqueda al que se hace referencia.You may use whichever method of locating the referenced search result item is most convenient for your application. En el código del tutorial, usamos answerType y resultIndex para ubicar cada resultado de búsqueda.In our tutorial code, we use the answerType and resultIndex to locate each search result.

Por último, ha llegado el momento de echar un vistazo a nuestra función renderSearchResults().Finally, it's time to look at our function renderSearchResults(). Esta función se itera en las tres colecciones rankingResponse que representan las tres secciones de los resultados de la búsqueda.This function iterates over the three rankingResponse collections that represent the three sections of the search results. En cada sección, llamamos a renderResultsItems() para que represente los resultados de esa sección.For each section, we call renderResultsItems() to render the results for that section.

// render the search results given the parsed JSON response
function renderSearchResults(results) {

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

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

Renderizar elementos de resultadoRendering result items

En nuestro código JavaScript es un objeto, searchItemRenderers, que contiene representadores: funciones que generan código HTML para cada tipo de resultado de búsqueda.In our JavaScript code is an object, searchItemRenderers, that contains renderers: functions that generate HTML for each kind of search result.

searchItemRenderers = { 
    entities: function(item) { ... },
    places: function(item) { ... }
}

Una función de representador puede aceptar los parámetros siguientes:A renderer function may accept the following parameters:

item El objeto de JavaScript que contiene las propiedades del elemento, como su dirección URL y la descripción.The JavaScript object containing the item's properties, such as its URL and its description.
index El índice del elemento de resultado dentro de su colección.The index of the result item within its collection.
count El número de elementos de la colección del elemento de resultado de la búsqueda.The number of items in the search result item's collection.

Los parámetros index y count pueden usarse para numerar los resultados, para generar un código HTML especial para el principio o el final de una colección, para insertar saltos de línea después de cierto número de elementos, etc.The index and count parameters can be used to number results, to generate special HTML for the beginning or end of a collection, to insert line breaks after a certain number of items, and so on. Si un representador no necesita esta funcionalidad, no es necesario aceptar estos dos parámetros.If a renderer does not need this functionality, it does not need to accept these two parameters. De hecho, no los usamos en los representadores de nuestra aplicación del tutorial.In fact, we do not use them in the renderers for our tutorial app.

Observemos más de cerca el representador entities:Let's take a closer look at the entities renderer:

    entities: function(item) {
        var html = [];
        html.push("<p class='entity'>");
        if (item.image) {
            var img = item.image;
            if (img.hostPageUrl) html.push("<a href='" + img.hostPageUrl + "'>");
            html.push("<img src='" + img.thumbnailUrl +  "' title='" + img.name + "' height=" + img.height + " width= " + img.width + ">");
            if (img.hostPageUrl) html.push("</a>");
            if (img.provider) {
                var provider = img.provider[0];
                html.push("<small>Image from ");
                if (provider.url) html.push("<a href='" + provider.url + "'>");
                html.push(provider.name ? provider.name : getHost(provider.url));
                if (provider.url) html.push("</a>");
                html.push("</small>");
            }
        }
        html.push("<p>");
        if (item.entityPresentationInfo) {
            var pi = item.entityPresentationInfo;
            if (pi.entityTypeHints || pi.entityTypeDisplayHint) {
                html.push("<i>");
                if (pi.entityTypeDisplayHint) html.push(pi.entityTypeDisplayHint);
                else if (pi.entityTypeHints) html.push(pi.entityTypeHints.join("/"));
                html.push("</i> - ");
            }
        }
        html.push(item.description);
        if (item.webSearchUrl) html.push("&nbsp;<a href='" + item.webSearchUrl + "'>More</a>")
        if (item.contractualRules) {
            html.push("<p><small>");
            var rules = [];
            for (var i = 0; i < item.contractualRules.length; i++) {
                var rule = item.contractualRules[i];
                var link = [];
                if (rule.license) rule = rule.license;
                if (rule.url) link.push("<a href='" + rule.url + "'>");
                link.push(rule.name || rule.text || rule.targetPropertyName + " source");
                if (rule.url) link.push("</a>");
                rules.push(link.join(""));
            }
            html.push("License: " + rules.join(" - "));
            html.push("</small>");
        }
        return html.join("");
    }, // places renderer omitted

La función del representador de imágenes:Our entity renderer function:

  • Compila la etiqueta HTML <img> para mostrar la miniatura de la imagen, si la hubiera.Builds the HTML <img> tag to display the image thumbnail, if any.
  • Compila la etiqueta <a> HTML vinculada a la página que contiene la imagen.Builds the HTML <a> tag that links to the page that contains the image.
  • Compila la descripción que muestra información sobre la imagen y el sitio en que se encuentra.Builds the description that displays information about the image and the site it's on.
  • Incorpora una clasificación de la entidad mediante las sugerencias de presentación, si las hubiera.Incorporates the entity's classification using the display hints, if any.
  • Incluye un vínculo a una búsqueda de Bing para obtener más información sobre la entidad.Includes a link to a Bing search to get more information about the entity.
  • Muestra cualquier información de licencia o atribución que requieran las fuentes de datos.Displays any licensing or attribution information required by data sources.

Id. de cliente persistentePersisting client ID

Las respuestas de Bing Search API pueden incluir un encabezado X-MSEdge-ClientID que debe devolverse a la API con las solicitudes sucesivas.Responses from the Bing search APIs may include a X-MSEdge-ClientID header that should be sent back to the API with successive requests. Si se utilizan varias instancias de Bing Search API, se debe usar el mismo identificador de cliente con todas ellas, si es posible.If multiple Bing Search APIs are being used, the same client ID should be used with all of them, if possible.

Especificar el encabezado X-MSEdge-ClientID permite a las API de Bing asociar todas las búsquedas de un usuario, lo que tiene dos ventajas importantes.Providing the X-MSEdge-ClientID header allows the Bing APIs to associate all of a user's searches, which has two important benefits.

En primer lugar, permite al motor de búsqueda de Bing aplicar un contexto pasado a las búsquedas, para así buscar resultados que satisfagan mejor al usuario.First, it allows the Bing search engine to apply past context to searches to find results that better satisfy the user. Si un usuario ha buscado previamente términos relacionados con la navegación, por ejemplo, una búsqueda posterior de "muelles" podría devolver información acerca de los muelles que se usan para dejar un velero.If a user has previously searched for terms related to sailing, for example, a later search for "docks" might preferentially return information about places to dock a sailboat.

En segundo lugar, Bing puede seleccionar aleatoriamente usuarios para disfrutar de nuevas características antes de que estén disponibles para el público.Second, Bing may randomly select users to experience new features before they are made widely available. Proporcionar el mismo identificador de cliente con cada solicitud garantiza que los usuarios elegidos para ver una característica la vean siempre.Providing the same client ID with each request ensures that users that have been chosen to see a feature always see it. Sin el identificador de cliente, el usuario puede ver una característica aparecer y desaparecer, de forma aparentemente aleatoria, en los resultados de búsqueda.Without the client ID, the user might see a feature appear and disappear, seemingly at random, in their search results.

Las directivas de seguridad del explorador (CORS) pueden impedir que el encabezado X-MSEdge-ClientID esté disponible para JavaScript.Browser security policies (CORS) may prevent the X-MSEdge-ClientID header from being available to JavaScript. Esta limitación tiene lugar cuando la respuesta a la búsqueda tiene un origen distinto al de la página que la solicitó.This limitation occurs when the search response has a different origin from the page that requested it. En un entorno de producción, debería abordar esta directiva mediante el hospedaje de un script de lado servidor que realice la llamada API en el mismo dominio que la página web.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. Como el script tiene el mismo origen que la página web, el encabezado X-MSEdge-ClientID está disponible para JavaScript.Since the script has the same origin as the Web page, the X-MSEdge-ClientID header is then available to JavaScript.

Nota

En una aplicación web de producción, debe realizar la solicitud del lado servidor de todos modos.In a production Web application, you should perform the request server-side anyway. En caso contrario, es preciso incluir la clave de Bing Search API en la página web, donde está disponible para cualquiera que vea el origen.Otherwise, your Bing Search API key must be included in the Web page, where it is available to anyone who views source. Se le facturará todo el uso bajo su clave de suscripción a API, incluso las solicitudes que realicen partes no autorizadas, por lo que es importante no exponer su clave.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.

Para fines de desarrollo, puede realizar la solicitud de Bing Web Search API a través de un proxy CORS.For development purposes, you can make the Bing Web Search API request through a CORS proxy. La respuesta de un proxy de este tipo tiene un encabezado Access-Control-Expose-Headers que agrega los encabezados de respuesta a listas blancas y hace que estén disponibles para JavaScript.The response from such a proxy has an Access-Control-Expose-Headers header that whitelists response headers and makes them available to JavaScript.

Es fácil instalar un proxy CORS para permitir que nuestra aplicación de tutorial acceda al encabezado de identificador de cliente.It's easy to install a CORS proxy to allow our tutorial app to access the client ID header. En primer lugar, si aún no lo tiene, instale Node.js.First, if you don't already have it, install Node.js. Escriba el comando siguiente en una ventana de comandos:Then issue the following command in a command window:

npm install -g cors-proxy-server

A continuación, cambie el punto de conexión de Bing Web Search del archivo HTML a:Next, change the Bing Web Search endpoint in the HTML file to:

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

Por último, inicie el proxy CORS con el siguiente comando:Finally, start the CORS proxy with the following command:

cors-proxy-server

Deje abierta la ventana de comandos mientras usa la aplicación del tutorial, ya que si la cierra, se detendrá el proxy.Leave the command window open while you use the tutorial app; closing the window stops the proxy. En la sección de encabezados HTTP expandibles situada bajo los resultados de la búsqueda, puede ver el encabezado X-MSEdge-ClientID (entre otras cosas) y comprobar que es el mismo en todas las solicitudes.In the expandable HTTP Headers section below the search results, you can now see the X-MSEdge-ClientID header (among others) and verify that it is the same for each request.

Pasos siguientesNext steps