Tutorial: Aplicativo Web de página única
Aviso
Em 30 de outubro de 2020, as APIs de Pesquisa do Bing foram migradas dos serviços de IA do Azure para os Serviços de Pesquisa do Bing. Esta documentação é fornecida apenas para referência. Para obter a documentação atualizada, consulte a documentação da API de pesquisa do Bing. Para obter instruções sobre como criar novos recursos do Azure para a Pesquisa do Bing, consulte Criar um recurso de Pesquisa do Bing por meio do Azure Marketplace.
A API de Pesquisa de Entidade do Bing permite pesquisar na Web informações sobre entidades e locais. É possível solicitar qualquer tipo de resultado, ou ambos, em uma determinada consulta. As definições de entidades e locais são fornecidas abaixo.
Result | Descrição |
---|---|
Entidades | Pessoas conhecidas, locais e coisas que você localiza pelo nome |
Locais | Restaurantes, hotéis e outras empresas locais que você encontra por nome ou por tipo (restaurantes italianos) |
Neste tutorial, compilamos um aplicativo Web de página única que usa a API de Pesquisa de Entidade do Bing para exibir os resultados da pesquisa diretamente na página. O aplicativo inclui componentes HTML, CSS e JavaScript.
A API permite que você priorize os resultados por local. Em um aplicativo móvel, você pode perguntar ao dispositivo sua própria localização. Em um aplicativo Web, é possível usar a função getPosition()
. Mas essa chamada funciona apenas em contextos seguros, e pode não fornecer uma localização precisa. Além disso, o usuário pode querer procurar entidades próximas a um local diferente do seu.
Nosso aplicativo, portanto, chama o serviço Bing Mapas para obter latitude e longitude de uma localização inserida pelo usuário. O usuário pode inserir o nome de um ponto de referência ("Space Needle") ou um endereço completo ou parcial ("Cidade de Nova Iorque"), e a API do Bing Mapas fornece as coordenadas.
Observação
Os cabeçalhos JSON e HTTP na parte inferior da página revelam as informações da resposta JSON e da solicitação HTTP quando recebem um clique. Esses detalhes são úteis ao explorar o serviço.
O aplicativo de tutorial ilustra como:
- Executar uma chamada à API de Pesquisa de Entidade do Bing em JavaScript
- Executar uma chamada à API do
locationQuery
Bing Mapas em JavaScript - Passar opções de pesquisa para as chamadas à API
- Exibir os resultados da pesquisa
- Manipular a ID do cliente do Bing e a chave de assinatura da API
- Lidar com quaisquer erros que possam ocorrer
A página do tutorial é completamente autossuficiente; ela não usa estruturas externas, folhas de estilo, nem mesmo arquivos de imagem. Ela usa apenas recursos de linguagem JavaScript amplamente compatíveis e funciona com as versões atuais de todos os principais navegadores da Web.
Neste tutorial, abordaremos somente as partes selecionadas do código-fonte. O código-fonte completo está disponível em uma página separada. Copie e cole esse código em um editor de texto e salve-o como bing.html
.
Observação
Este tutorial é muito semelhante ao tutorial de aplicativo da Pesquisa na Web do Bing de página única, mas lida apenas com os resultados da pesquisa de entidade.
Pré-requisitos
Para acompanhar o tutorial, você precisará de chaves de assinatura para a API de Pesquisa do Bing e da API do Bing Mapas.
- Uma assinatura do Azure – crie uma gratuitamente
- Depois de obter a assinatura do Azure:
- Crie um recurso de Pesquisa do Bing no portal do Azure para obter a chave e o ponto de extremidade. Após a implantação, clique em Ir para o recurso.
- Crie um recurso do Bing Mapas no portal do Azure para obter a chave e o ponto de extremidade. Após a implantação, clique em Ir para o recurso.
Componentes do aplicativo
Como qualquer aplicativo Web de página única, o aplicativo de tutorial inclui três partes:
- HTML – define a estrutura e o conteúdo da página
- CSS – define a aparência da página
- JavaScript – define o comportamento da página
Este tutorial não aborda a maior parte do CSS ou do HTML em detalhes, pois eles são simples.
O HTML contém o formulário de pesquisa no qual o usuário insere uma consulta e escolhe as opções de pesquisa. O formulário está conectado ao JavaScript que, de fato, executa a pesquisa pelo atributo onsubmit
da marca <form>
:
<form name="bing" onsubmit="return newBingEntitySearch(this)">
O manipulador onsubmit
retorna false
, o que impede o envio do formulário para um servidor. Na verdade, o código JavaScript, faz o trabalho de coletar as informações necessárias do formulário e realizar a pesquisa.
A pesquisa é feita em duas fases. Primeiro, se o usuário tiver inserido uma restrição de local, uma consulta do Bing Mapas será feita para converter em coordenadas. O retorno de chamada para essa consulta, em seguida, inicia a consulta de Pesquisa de Entidade do Bing.
O HTML também contém as divisões (marcas HTML <div>
) nas quais os resultados da pesquisa são exibidos.
Gerenciar a chave de assinatura
Observação
Este aplicativo exige chaves de assinatura para ambas as API de Pesquisa do Bing e API do Bing Mapas.
Para evitar a necessidade de incluir as chaves de assinatura da Pesquisa do Bing e da API do Bing Mapas no código, usamos o armazenamento persistente do navegador para armazená-las. Se uma das chaves não foi armazenada, solicitamos e armazenamos para uso posterior. Se a chave for posteriormente rejeitada pela API, invalidamos a chave armazenada para que o usuário seja solicitado na próxima pesquisa.
Definimos as funções storeValue
e retrieveValue
que usam o objeto localStorage
(se o navegador der suporte) ou um cookie. A função getSubscriptionKey()
usa essas funções para armazenar e recuperar a chave do usuário. Você pode usar o ponto de extremidade global abaixo ou o ponto de extremidade de subdomínio personalizado exibido no portal do Azure para seu recurso.
// 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");
}
A marca HTML <body>
inclui um atributo onload
que chama getSearchSubscriptionKey()
e getMapsSubscriptionKey()
quando a página termina de ser carregada. Essas chamadas servem para solicitar imediatamente ao usuário as chaves, caso ainda não tenham entrado nelas.
<body onload="document.forms.bing.query.focus(); getSearchSubscriptionKey(); getMapsSubscriptionKey();">
Selecionar as opções de pesquisa
O formulário HTML inclui os seguintes controles:
Control | Descrição |
---|---|
where |
Um menu suspenso para selecionar o mercado (local e idioma) usado para a pesquisa. |
query |
O campo de texto no qual inserir os termos de pesquisa. |
safe |
Uma caixa de seleção que indica se o SafeSearch está ativado (restringe os resultados "adulto") |
what |
Um menu para escolher pesquisar entidades, locais ou ambos. |
mapquery |
O campo de texto no qual o usuário pode inserir um endereço completo ou parcial, um ponto de referência, etc. para ajudar a Pesquisa de Entidade do Bing retornar resultados mais relevantes. |
Observação
Atualmente, os resultados estão disponíveis apenas nos Estados Unidos. Os menus where
e what
têm código para impor essa restrição. Se você escolher um mercado não EUA enquanto Locais estiver selecionado no menu what
, what
altera para Tudo. Se você escolher Locais enquanto um mercado não EUA estiver selecionado no menu where
, where
altera para os EUA.
A função JavaScript bingSearchOptions()
converte esses campos em uma cadeia de caracteres de consulta parcial para a API de Pesquisa do Bing.
// 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 exemplo, o recurso SafeSearch pode ser strict
, moderate
ou off
, com moderate
sendo o padrão. Porém, nosso formulário usa uma caixa de seleção, que tem apenas dois estados. O código JavaScript converte essa configuração para strict
ou off
(não usamos moderate
).
O campo mapquery
não é tratado em bingSearchOptions()
porque é usado para a consulta de localização do Bing Mapas, não para a Pesquisa de Entidade do Bing.
Obter um local
A API do Bing Mapas oferece um locationQuery
método, que usamos para localizar a latitude e longitude do local inserido pelo usuário. Essas coordenadas são então passadas para a API de Pesquisa de Entidade do Bing com a solicitação do usuário. Os resultados da pesquisa priorizam entidades e lugares próximos ao local especificado.
Não podemos acessar a API do Bing Mapas usando uma consulta XMLHttpRequest
comum em um aplicativo Web, pois o serviço não dá suporte a consultas entre origens. Felizmente, há suporte para JSONP (o "P" é para "preenchido"). Uma resposta JSONP é uma resposta JSON comum envolvida em uma chamada de função. A solicitação é feita, inserindo uma marca <script>
no documento. (Carregar scripts não está sujeito a políticas de segurança do navegador.)
A função bingMapsLocate()
cria e insere a marca <script>
para a consulta. O segmento jsonp=bingMapsCallback
da cadeia de caracteres de consulta especifica o nome da função a ser chamada com a resposta.
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;
}
Observação
Se a API do Bing Mapas não responder, a função bingMapsCallBack()
nunca será chamada. Normalmente, isso significa que bingEntitySearch()
não é chamado e os resultados da pesquisa de entidade não aparecem. Para evitar esse cenário, bingMapsLocate()
também define um temporizador para chamar bingEntitySearch()
após cinco segundos. Há lógica na função de retorno de chamada para evitar a realização da pesquisa de entidade duas vezes.
Quando a consulta é concluída, a função bingMapsCallback()
é chamada, conforme solicitado.
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());
}
Juntamente com latitude e longitude, a consulta da Pesquisa de Entidade do Bing requer um raio que indique a precisão das informações do local. Calculamos o raio usando a caixa delimitadora fornecida na resposta do Bing Mapas. A caixa delimitadora é um retângulo que envolve todo o local. Por exemplo, se o usuário inserir NYC
, o resultado conterá aproximadamente coordenadas centrais de Nova Iorque e uma caixa delimitadora que engloba a cidade.
Primeiro, calculamos as distâncias das coordenadas primárias para cada um dos quatro cantos da caixa delimitadora usando a função haversineDistance()
(não mostrado). Nós usamos a maior dessas quatro distâncias como o raio. O raio mínimo é um quilômetro. Esse valor também será usado como padrão, se nenhuma caixa delimitadora for fornecida na resposta.
Tendo obtido as coordenadas e o raio, nós chamamos bingEntitySearch()
para realizar a pesquisa atual.
Executar a pesquisa
Fornecida a consulta, um local, uma cadeia de caracteres de opções e a chave da API, a função BingEntitySearch()
fará a solicitação da Pesquisa de Entidade do Bing.
// 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;
}
Após a conclusão com êxito da solicitação HTTP, o JavaScript chamará o manipulador de eventos load
, a função handleBingResponse()
, para manipular uma solicitação HTTP GET com êxito para a 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
Uma solicitação HTTP com êxito não significa necessariamente que a pesquisa ocorreu com êxito. Se ocorrer um erro na operação de pesquisa, a API de Pesquisa de Entidade do Bing retornará um código de status HTTP diferente de 200 e incluirá informações de erro na resposta JSON. Além disso, se a solicitação tinha limite de taxa, a API retorna uma resposta vazia.
Grande parte do código em ambas as funções anteriores é dedicada ao tratamento de erro. Erros podem ocorrer nos seguintes estágios:
Estágio | Erro(s) potencial(is) | Manipulado por |
---|---|---|
Compilar objeto de solicitação JavaScript | URL inválida | Bloco try /catch |
Fazer a solicitação | Erros de rede, conexões anuladas | Manipuladores de eventos error e abort |
Executar a pesquisa | Solicitação inválida, JSON inválido, limites de taxa | testes no manipulador de eventos load |
Os erros são tratados com uma chamada a renderErrorMessage()
com os detalhes conhecidos sobre o erro. Se a resposta passar no desafio completo de testes de erro, chamamos renderSearchResults()
para exibir os resultados da pesquisa na página.
Exibir os resultados da pesquisa
A API de Pesquisa de Entidade do Bing exige a exibição dos resultados em uma ordem especificada. Como a API pode retornar dois tipos diferentes de respostas, não é suficiente fazer uma iteração na coleção Entities
ou Places
de nível superior na resposta JSON e exibir esses resultados. (Se você quiser apenas um tipo de resultado, use o parâmetro de consulta responseFilter
.)
Em vez disso, usamos a coleção rankingResponse
nos resultados da pesquisa para ordenar os resultados para exibição. Esse objeto refere-se a itens nas coleções Entitiess
e/ou Places
.
rankingResponse
pode conter até três coleções de resultados da pesquisa, designadas pole
, mainline
e sidebar
.
pole
, se estiver presente, será o resultado de pesquisa mais relevante e deverá ser exibido em destaque.
mainline
refere-se à maior parte dos resultados da pesquisa. Os resultados da linha principal devem ser exibidos imediatamente após pole
(ou primeiro, caso pole
não esteja presente).
Por fim.
sidebar
refere-se aos resultados da pesquisa auxiliares. Eles podem ser exibidos em uma barra lateral real ou simplesmente após os resultados da linha principal. Nós escolhemos o último para o nosso aplicativo de tutorial.
Cada item em uma coleção rankingResponse
refere-se aos itens do resultado da pesquisa real de duas formas diferentes, mas equivalentes.
Item | Descrição |
---|---|
id |
O tipo id é semelhante a uma URL, mas não deve ser usado para links. O tipo id de um resultado de classificação corresponde à id de um item de resultado da pesquisa em uma coleção de respostas ou a uma coleção inteira de respostas (como Entities ). |
answerType resultIndex |
O answerType refere-se à coleção de respostas de nível superior que contém o resultado (por exemplo, Entities ). O resultIndex refere-se ao índice do resultado nessa coleção. Se resultIndex estiver omitido, o resultado da classificação refere-se à coleção inteira. |
Observação
Para obter mais informações sobre essa parte da resposta da pesquisa, confira Resultados de Classificação.
Use qualquer método de localização do item de resultado da pesquisa referenciado que seja mais conveniente para o aplicativo. Em nosso código do tutorial, usamos answerType
e resultIndex
para localizar cada resultado da pesquisa.
Por fim, é hora de examinar nossa função renderSearchResults()
. Essa função itera pelas três coleções rankingResponse
que representam as três seções dos resultados da pesquisa. Para cada seção, chamamos renderResultsItems()
para renderizar os resultados para essa seção.
// 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 os itens de resultado
Em nosso código JavaScript há um objeto, searchItemRenderers
, que contém renderizadores: funções que geram um HTML para cada tipo de resultado da pesquisa.
searchItemRenderers = {
entities: function(item) { ... },
places: function(item) { ... }
}
Uma função de renderizador pode aceitar os seguintes parâmetros:
Parâmetro | Descrição |
---|---|
item |
O objeto do JavaScript que contém as propriedades do item, como a URL e a descrição. |
index |
O índice do item de resultado na coleção. |
count |
O número de itens na coleção do item de resultado da pesquisa. |
Os parâmetros index
e count
podem ser usados para numerar os resultados, para gerar um HTML especial no início ou no fim de uma coleção, para inserir quebras de linha após determinado número de itens e assim por diante. Se um renderizador não precisar dessa funcionalidade, não precisará aceitar esses dois parâmetros. Na verdade, não os utilizamos nos renderizadores do nosso aplicativo de tutorial.
Vamos examinar o renderizador entities
mais detalhadamente:
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(" <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
Nossa função de renderizador de entidade:
- Compila a marca HTML
<img>
para exibir a miniatura da imagem, se houver. - Compila a marca HTML
<a>
vinculada à página que contém a imagem. - Compila a descrição que exibe informações sobre a imagem e o site no qual ela está localizada.
- Incorpora a classificação da entidade usando as dicas de exibição, se houver.
- Inclui um link para uma pesquisa do Bing para obter mais informações sobre a entidade.
- Exibe qualquer informação de licenciamento ou atribuição exigida por fontes de dados.
Persistir ID do cliente
As respostas das APIs de Pesquisa do Bing podem incluir um cabeçalho X-MSEdge-ClientID
que deve ser enviado novamente para a API com solicitações sucessivas. Se várias APIs de Pesquisa do Bing estiverem sendo usadas, a mesma ID do cliente deverá ser usada com todas elas, se possível.
Fornecer o cabeçalho X-MSEdge-ClientID
permite que as APIs do Bing associem todas as pesquisas do usuário, o que oferece dois benefícios importantes.
Primeiro, ele permite que o mecanismo de pesquisa do Bing aplique o contexto passado às pesquisas para localizar resultados que melhor atendam às expectativas do usuário. Se um usuário tiver pesquisado termos relacionados à navegação anteriormente, por exemplo, uma pesquisa posterior por "nós" poderá, preferencialmente, retornar informações sobre os nós usados em navegação.
Em segundo lugar, o Bing pode selecionar aleatoriamente usuários para experimentar novos recursos antes de serem amplamente disponibilizados. O fornecimento da mesma ID do cliente com cada solicitação garante que os usuários que foram escolhidos para ver um recurso sempre o vejam. Sem a ID do cliente, o usuário poderá ver um recurso aparecer e desaparecer, aparentemente de forma aleatória, nos resultados da pesquisa.
As políticas de segurança do navegador (CORS) podem impedir que o cabeçalho X-MSEdge-ClientID
fique disponível para JavaScript. Essa limitação ocorre quando a resposta da pesquisa tem uma origem diferente da página que a solicitou. Em um ambiente de produção, você deve lidar com essa política hospedando um script do servidor que faz a chamada à API no mesmo domínio da página da Web. Como o script tem a mesma origem da página da Web, o cabeçalho X-MSEdge-ClientID
estará disponível para JavaScript.
Observação
Em um aplicativo Web de produção, você deve executar a solicitação do lado do servidor de qualquer maneira. Caso contrário, a chave da API de Pesquisa do Bing deverá ser incluída na página da Web, onde estará disponível para qualquer pessoa que exibir a origem. Você é cobrado por todos os usos em sua chave de assinatura de API, até mesmo por solicitações feitas por partes não autorizadas. Portanto, é importante não expor sua chave.
Para fins de desenvolvimento, você pode fazer a solicitação da API de Pesquisa na Web do Bing por meio de um proxy CORS. A resposta desse proxy tem um cabeçalho Access-Control-Expose-Headers
que permite listas de cabeçalhos de resposta e as disponibiliza em JavaScript.
É fácil instalar um proxy CORS para permitir que o aplicativo de tutorial acesse o cabeçalho da ID do cliente. Primeiro, caso ainda não tenha, instale o Node.js. Em seguida, emita o seguinte comando em uma janela de comando:
npm install -g cors-proxy-server
Em seguida, altere o ponto de extremidade da Pesquisa na Web do Bing no arquivo HTML para:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search
Por fim, inicie o proxy CORS com o seguinte comando:
cors-proxy-server
Deixe a janela de comando aberta enquanto você usa o aplicativo de tutorial, já que se fechar a janela irá parar o proxy. Na seção Cabeçalhos HTTP expansíveis abaixo dos resultados da pesquisa, é possível ver o cabeçalho X-MSEdge-ClientID
(entre outros) e verificar se é o mesmo para cada solicitação.