Öğretici: Tek sayfalı web uygulaması oluşturma
Uyarı
30 Ekim 2020'de Bing Arama API'leri Azure yapay zeka hizmetlerinden Bing Arama Hizmetlerine taşındı. Bu belgeler yalnızca başvuru için sağlanır. Güncelleştirilmiş belgeler için Bing arama API'sinin belgelerine bakın. Bing araması için yeni Azure kaynakları oluşturma yönergeleri için bkz. Azure Market aracılığıyla Bing Arama kaynağı oluşturma.
Bing Haber Arama API'si Web'de arama yapmanızı ve arama sorgusuna uyan haber türündeki sonuçları almanızı sağlar. Bu öğreticide, Bing Haber Arama API'sini kullanarak sayfada arama sonuçlarını görüntüleyen tek sayfalı bir Web uygulaması oluşturuyoruz. Uygulama HTML, CSS ve JavaScript bileşenlerini içeriyor. Bu örneğin kaynak kodu GitHub'da kullanılabilir.
Not
Sayfanın en altındaki JSON ve HTTP başlıklarına tıklandığında, JSON yanıt ve HTTP istek bilgileri gösterilir. Bu ayrıntılar hizmeti keşfederken yararlı olabilir.
Öğretici uygulaması şunları gösterir:
- JavaScript'te Bing Haber Arama API'sine çağrı yapma
- Arama seçeneklerini Bing Haber API'sine geçirme
- Dört kategoriye ait haber araması sonuçlarını görüntüleme: 24 saat, son bir hafta, ay veya tüm zamanlar için herhangi bir tür, iş dünyası, sağlık veya siyaset haberleri
- Arama sonuçları sayfalarında gezinme
- Bing istemci kimliğini ve API abonelik anahtarını işleme
- Oluşabilecek hataları işleme
Öğretici sayfası tamamen bağımsızdır; dışarıdan hiçbir kare, stil sayfası veya resim dosyası kullanmaz. Yalnızca yaygın olarak desteklenen JavaScript dilinin özelliklerini kullanır ve tüm önemli Web tarayıcılarının geçerli sürümlerinde çalışır.
Önkoşullar
Öğreticiyi takip etmek için Bing Arama API'sinin abonelik anahtarlarına ihtiyacınız vardır. Bunlara sahip değilseniz bunları oluşturmanız gerekir:
- Azure aboneliği - Ücretsiz bir abonelik oluşturun
- Azure aboneliğinizi aldıktan sonra anahtarınızı ve uç noktanızı almak için Azure portal bir Bing Arama kaynağı oluşturun. Dağıtıldıktan sonra Kaynağa git'e tıklayın.
Uygulama bileşenleri
Tüm tek sayfalı Web uygulamaları gibi bu öğreticinin uygulaması da üç bölümden oluşur:
- HTML - Sayfanın yapısını ve içeriğini tanımlar
- CSS - Sayfanın görünümünü tanımlar
- JavaScript - Sayfanın davranışını tanımlar
HTML ile CSS'nin büyük bölümü bilinen şeylerdir, dolayısıyla öğreticide bunlar açıklanmayacak. HTML, kullanıcının sorgu girdiği ve arama seçeneklerini belirttiği bir arama formu içerir. Form, <form>
etiketinin onsubmit
özniteliğini kullanarak aramayı gerçekleştiren JavaScript'e bağlanır:
<form name="bing" onsubmit="return newBingNewsSearch(this)">
onsubmit
işleyicisi, formun sunucuya gönderilmesini engelleyen false
değerini döndürür. JavaScript kodu, formdan gerekli bilgileri toplama ve aramayı gerçekleştirme çalışmalarını yapar.
HTML, arama sonuçlarının gösterildiği bölümleri de (HTML <div>
etiketleri) içerir.
Abonelik anahtarını yönetme
Bing Arama API'si abonelik anahtarını koda eklemek zorunda kalmamak için, tarayıcının kalıcı depolamasını kullanarak anahtarı depolarız. Anahtar depolanmadan önce, kullanıcıdan anahtarı isteriz. Anahtar daha sonra API tarafından reddedilirse, depolanan anahtarı geçersiz kılarız. Böylelikle kullanıcıdan yeniden anahtar istenir.
localStorage
nesnesini (tüm tarayıcılar bunu desteklemez) veya bir tanımlama bilgisi kullanan storeValue
ve retrieveValue
işlevlerini tanımlarız. getSubscriptionKey()
işlevi, bu işlevleri kullanarak kullanıcının anahtarını depolar ve alır. Aşağıdaki genel uç noktayı veya kaynağınızın Azure portal görüntülenen özel alt etki alanı uç noktasını kullanabilirsiniz.
// Cookie names for data we store
API_KEY_COOKIE = "bing-search-api-key";
CLIENT_ID_COOKIE = "bing-search-client-id";
// Bing Search API endpoint
BING_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/news";
// ... omitted definitions of storeValue() and retrieveValue()
// Browsers differ in their support for persistent storage by
// local HTML files. See the source code for browser-specific
// options.
// Get stored API subscription key, or
// prompt if it's not found.
function getSubscriptionKey() {
var key = retrieveValue(API_KEY_COOKIE);
while (key.length !== 32) {
key = prompt("Enter Bing Search API subscription key:", "").trim();
}
// always set the cookie in order to update the expiration date
storeValue(API_KEY_COOKIE, key);
return key;
}
HTML <form>
etiketi onsubmit
arama sonuçlarını döndürmek için bingWebSearch
işlevini çağırır. bingWebSearch
, her sorgunun kimliğini doğrulamak için getSubscriptionKey()
kullanır. Önceki tanımda gösterildiği gibi, anahtar girilmediyse getSubscriptionKey
kullanıcıdan anahtarı ister. Ardından anahtar uygulama tarafından sürekli kullanılmak üzere depolanır.
<form name="bing" onsubmit="this.offset.value = 0; return bingWebSearch(this.query.value,
bingSearchOptions(this), getSubscriptionKey())">
Arama seçeneklerini belirtme
Aşağıdaki şekilde sorgu metin kutusu ile bursla ilgili haberler için bir arama tanımlayan seçenekler gösterilir.
HTML formu, adları aşağıda gösterilen öğeleri içerir:
Öğe | Açıklama |
---|---|
where |
Aramada kullanılan pazarı (konum ve dil) seçmek için açılan menü. |
query |
Arama terimlerinin girileceği metin alanı. |
category |
Belirli sonuç türlerini öne çıkarmak için onay kutuları. Örneğin Sağlık türünün öne çıkarılması sağlık haberlerinin daha üst sıralarda görüntülenmesini sağlar. |
when |
Aramayı isteğe bağlı olarak en son günle, haftayla veya ayla sınırlamak için açılan menü. |
safe |
"Yetişkinlere yönelik" sonuçları filtrelemek için Bing Güvenli Arama özelliğinin kullanılıp kullanılmayacağını belirten onay kutusu. |
count |
Gizli alan. Her istekte döndürülecek arama sonuçlarının sayısı. Sayfada daha az veya daha fazla sonuç görüntülemek için bunu değiştirin. |
offset |
Gizli alan. İstekteki ilk arama sonucunun göreli konumu; sayfalama için kullanılır. Yeni istekte 0 değerine sıfırlanır. |
Not
Bing Web Araması başka sorgu parametreleri de sunar. Biz yalnızca birkaç tanesini kullanıyoruz.
// build query options from the HTML form
function bingSearchOptions(form) {
var options = [];
options.push("mkt=" + form.where.value);
options.push("SafeSearch=" + (form.safe.checked ? "strict" : "off"));
if (form.when.value.length) options.push("freshness=" + form.when.value);
for (var i = 0; i < form.category.length; i++) {
if (form.category[i].checked) {
category = form.category[i].value;
break;
}
}
if (category.valueOf() != "all".valueOf()) {
options.push("category=" + category);
}
options.push("count=" + form.count.value);
options.push("offset=" + form.offset.value);
return options.join("&");
}
Örneğin, gerçek API çağrısındaki SafeSearch
parametresi strict
, moderate
veya off
olabilir; varsayılan değer moderate
ayarıdır. Öte yandan, formumuzda yalnızca iki durumu olan bir onay kutusu kullanılıyor. JavaScript kodu bu ayarı strict
veya off
ayarına dönüştürür (moderate
kullanılmaz).
İsteği gerçekleştirme
Sorgu, seçenekler dizesi ve API anahtarı verili durumdayken, BingNewsSearch
işlevi Bing Haber Arama uç noktasına isteği yöneltmek için bir XMLHttpRequest
nesnesi kullanır.
// perform a search given query, options string, and API key
function bingNewsSearch(query, options, key) {
// scroll to top of window
window.scrollTo(0, 0);
if (!query.trim().length) return false; // empty query, do nothing
showDiv("noresults", "Working. Please wait.");
hideDivs("results", "related", "_json", "_http", "paging1", "paging2", "error");
var request = new XMLHttpRequest();
if (category.valueOf() != "all".valueOf()) {
var queryurl = BING_ENDPOINT + "/search?" + "?q=" + encodeURIComponent(query) + "&" + options;
}
else
{
if (query){
var queryurl = BING_ENDPOINT + "?q=" + encodeURIComponent(query) + "&" + options;
}
else {
var queryurl = BING_ENDPOINT + "?" + options;
}
}
// open the request
try {
request.open("GET", queryurl);
}
catch (e) {
renderErrorMessage("Bad request (invalid URL)\n" + queryurl);
return false;
}
// add request headers
request.setRequestHeader("Ocp-Apim-Subscription-Key", key);
request.setRequestHeader("Accept", "application/json");
var clientid = retrieveValue(CLIENT_ID_COOKIE);
if (clientid) request.setRequestHeader("X-MSEdge-ClientID", clientid);
// event handler for successful response
request.addEventListener("load", handleBingResponse);
// event handler for erorrs
request.addEventListener("error", function() {
renderErrorMessage("Error completing request");
});
// event handler for aborted request
request.addEventListener("abort", function() {
renderErrorMessage("Request aborted");
});
// send the request
request.send();
return false;
}
HTTP isteği başarıyla tamamlanınca, API'ye yönelik başarılı HTTP GET isteğini işlemek için JavaScript load
olay işleyicisi olan handleBingResponse()
işlevini çağırır.
// 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 === "News") {
renderSearchResults(jsobj);
} else {
renderErrorMessage("No search results in JSON response");
}
} else {
renderErrorMessage("Empty response (are you sending too many requests too quickly?)");
}
}
// Any other HTTP response is an error
else {
// 401 is unauthorized; force re-prompt for API key for next request
if (this.status === 401) invalidateSubscriptionKey();
// some error responses don't have a top-level errors object, so gin one up
var errors = jsobj.errors || [jsobj];
var errmsg = [];
// display HTTP status code
errmsg.push("HTTP Status " + this.status + " " + this.statusText + "\n");
// add all fields from all error responses
for (var i = 0; i < errors.length; i++) {
if (i) errmsg.push("\n");
for (var k in errors[i]) errmsg.push(k + ": " + errors[i][k]);
}
// also display Bing Trace ID if it isn't blocked by CORS
var traceid = this.getResponseHeader("BingAPIs-TraceId");
if (traceid) errmsg.push("\nTrace ID " + traceid);
// and display the error message
renderErrorMessage(errmsg.join("\n"));
}
}
Önemli
Başarılı bir HTTP isteği, aramanın kendisinin başarılı olduğu anlamına gelmeyebilir. Arama işleminde hata oluşursa, Bing Haber Arama API'si 200 olmayan bir HTTP durum kodu döndürür ve JSON yanıtına hata bilgilerini ekler. Buna ek olarak, istekte hız sınırlaması varsa API boş yanıt döndürür.
Önceki işlevlerin ikisinde de kodun büyük bölümü hata işlemeye ayrılmıştır. Şu aşamalarda hata oluşabilir:
Aşama | Olası hatalar | İşleyen |
---|---|---|
JavaScript istek nesnesi oluşturma | Geçersiz URL | try /catch bloğu |
İstekte bulunma | Ağ hataları, durdurulan bağlantılar | error ve abort olay işleyicileri |
Aramayı gerçekleştirme | Geçersiz istek, geçersiz JSON, hız sınırları | load olay işleyicisindeki testler |
Hatalar, hata hakkındaki bilinen tüm ayrıntılarla birlikte renderErrorMessage()
çağrılarak işlenir. Yanıt tüm hata testlerinden geçerse, arama sonuçlarını sayfada görüntülemek için renderSearchResults()
işlevini çağırırız.
Arama sonuçlarını görüntüleme
Arama sonuçlarını görüntülemeye yönelik ana işlev renderSearchResults()
işlevidir. Bu işlev, Bing Haber Arama hizmeti tarafından döndürülen JSON'u alır, sonra da haber sonuçlarını ve varsa ilgili aramaları işler.
// render the search results given the parsed JSON response
function renderSearchResults(results) {
// add Prev / Next links with result count
var pagingLinks = renderPagingLinks(results);
showDiv("paging1", pagingLinks);
showDiv("paging2", pagingLinks);
showDiv("results", renderResults(results.value));
if (results.relatedSearches)
showDiv("sidebar", renderRelatedItems(results.relatedSearches));
}
Ana arama sonuçları JSON yanıtında en üst düzey value
nesnesi olarak döndürülür. Bunları renderResults()
işlevimize geçiririz. Bu işlev sonuçları tekrarlar ve her öğeyi HTML olarak işlemek için ayrı bir işlev çağırır. Sonuçta elde edilen HTML renderSearchResults()
işlevine döndürülür ve burada sayfadaki results
bölümüne eklenir.
function renderResults(items) {
var len = items.length;
var html = [];
if (!len) {
showDiv("noresults", "No results.");
hideDivs("paging1", "paging2");
return "";
}
for (var i = 0; i < len; i++) {
html.push(searchItemRenderers.news(items[i], i, len));
}
return html.join("\n\n");
}
Bing Haber Arama API'si, her biri kendi üst düzey nesnesinin içinde olmak üzere en çok dört farklı türde ilgili sonuç döndürür. Bunlar:
İlişki | Description |
---|---|
pivotSuggestions |
Özgün aramadaki asıl sözcüğü başka bir sözcükle değiştiren sorgular. Örneğin, "kırmızı çiçekler" araması yaparsanız pivot sözcüğü "kırmızı" ve pivot öneri de "sarı çiçekler" olabilir. |
queryExpansions |
Daha fazla terim ekleyerek özgün aramayı daraltan sorgular. Örneğin, "Microsoft Surface" araması yaparsanız genişletilmiş sorgu "Microsoft Surface Pro" olabilir. |
relatedSearches |
Özgün aramayı giren diğer kullanıcılar tarafından da girilmiş olan sorgular. Örneğin, "Mount Rainier" araması yaparsanız, ilgili arama "Mt. Saint Helens" olabilir. |
similarTerms |
Özgün aramayla benzer anlamı olan sorgular. Örneğin, "okullar" araması yaparsanız benzer bir terim "eğitim" olabilir. |
renderSearchResults()
için daha önce gördüğümüz gibi, yalnızca relatedItems
önerilerini işleriz ve sonuçta elde edilen bağlantıları sayfanın kenar çubuğuna yerleştiririz.
Sonuç öğelerini işleme
JavaScript kodunda searchItemRenderers
nesnesi, her tür arama sonucu için HTML oluşturan renderers: işlevlerini içerir.
searchItemRenderers = {
news: function(item) { ... },
webPages: function (item) { ... },
images: function(item, index, count) { ... },
relatedSearches: function(item) { ... }
}
İşleyici işlevi aşağıdaki parametreleri kabul edebilir:
Parametre | Açıklama |
---|---|
item |
Öğenin özelliklerini, örneğin URL'sini ve açıklamasını içeren JavaScript nesnesi. |
index |
Kendi koleksiyonu içindeki arama öğesinin dizini. |
count |
Arama sonucu öğesinin koleksiyonundaki öğelerin sayısı. |
Sonuçları saymak, koleksiyonun başlangıcı veya sonuna özel HTML oluşturmak ve belirli sayıdaki öğeden sonra satır sonu eklemek gibi işlemler için index
ve count
parametreleri kullanılabilir. İşleyicinin bu işleve ihtiyacı yoksa, bu iki parametreyi kabul etmesi gerekmez.
oluşturucu news
aşağıdaki JavaScript alıntısında gösterilmiştir:
// render news story
news: function (item) {
var html = [];
html.push("<p class='news'>");
if (item.image) {
width = 60;
height = Math.round(width * item.image.thumbnail.height / item.image.thumbnail.width);
html.push("<img src='" + item.image.thumbnail.contentUrl +
"&h=" + height + "&w=" + width + "' width=" + width + " height=" + height + ">");
}
html.push("<a href='" + item.url + "'>" + item.name + "</a>");
if (item.category) html.push(" - " + item.category);
if (item.contractualRules) { // MUST display source attributions
html.push(" (");
var rules = [];
for (var i = 0; i < item.contractualRules.length; i++)
rules.push(item.contractualRules[i].text);
html.push(rules.join(", "));
html.push(")");
}
html.push(" (" + getHost(item.url) + ")");
html.push("<br>" + item.description);
return html.join("");
},
Haber işleyici işlevi:
- Paragraf etiketi oluşturur, bu etiketi
news
sınıfına atar ve html dizisine gönderir. - Resmin küçük resim boyutunu hesaplar (genişlik 60 piksele sabitlenir, yükseklik buna orantılı olarak hesaplanır).
- Resmin küçük resmini görüntülemek için HTML
<img>
etiketini oluşturur. - Görüntüye ve bu görüntüyü içeren sayfaya bağlanan HTML
<a>
etiketlerini oluşturur. - Resim ve bu resmin bulunduğu site hakkındaki bilgileri görüntüleyen bir açıklama oluşturur.
Küçük resim boyutu hem <img>
etiketinde hem de küçük resmin URL'sindeki h
ve w
alanlarında kullanılır. Ardından Bing küçük resim hizmeti tam olarak bu boyutta bir küçük resim verir.
Kalıcı istemci kimliği
Bing arama API'lerinden gelen yanıtlar, başarılı isteklerle birlikte API'ye geri gönderilmesi gereken bir X-MSEdge-ClientID
üst bilgisi içerir. Birden çok Bing Arama API'si kullanılıyorsa, mümkün olduğunca bu API'lerin tümünde aynı istemci kimliği kullanılmalıdır.
Böylelikle X-MSEdge-ClientID
üst bilgisi sayesinde Bing API'leri kullanıcının tüm aramalarını ilişkilendirebilir. Bunun iki önemli avantajı vardır.
İlk olarak, Bing arama alt yapısının geçmiş bağlamı aramalara uygulayarak kullanıcıya daha uygun sonuçlar bulabilmesini sağlar. Kullanıcı daha önce yelkencilikle ilgili terim aramaları yaptıysa, daha sonra yapılan "düğümler" araması tercihen yelkencilikte kullanılan düğümler hakkında bilgi döndürebilir.
İkincisi, Bing yeni özellikleri geniş ölçekte kullanıma sunmadan önce bunları denemesi için rastgele kullanıcılar seçebilir. Her istekle birlikte aynı istemci kimliğinin sağlanması, özelliği gören kullanıcıların bunu sürekli görebilmesini sağlar. İstemci kimliği olmadan, özellik kullanıcıya arama sonuçlarında rastgele gösterilebilir ve gösterilmeyebilir.
Tarayıcı güvenlik ilkeleri (CORS) X-MSEdge-ClientID
üst bilgisinin JavaScript'in kullanımına sunulmasını engelleyebilir. Bu sınırlama, arama sonucunun kaynağı istekte bulunan sayfadan farklı olduğunda ortaya çıkar. Üretim ortamında, Web sayfasıyla aynı etki alanında API çağrısı yapan bir sunucu tarafı betiği barındırarak bu ilkeye uymalısınız. Betiğin kaynağı Web sayfasıyla aynı olduğundan, X-MSEdge-ClientID
üst bilgisi JavaScript'in kullanımına sunulur.
Not
Üretim ortamındaki bir Web uygulamasında, isteği sunucu tarafından gerçekleştirmeniz gerekir. Aksi takdirde, Bing Arama API'si anahtarınızın Web sayfasına eklenmesi gerekir ve bu durumda kaynağı görüntüleyen herkes tarafından görülebilir. API abonelik anahtarınız altında gerçekleştirilen tüm kullanım, yetkisiz tarafların yaptığı istekler bile size faturalandırılır; dolayısıyla anahtarınızı açıklamamanız önemlidir.
Geliştirme amacıyla, Bing Web Araması API’si isteğini CORS ara sunucusu aracılığıyla yapabilirsiniz. Böyle bir proxy'den gelen yanıt, yanıt üst bilgilerine izin veren ve Bunları JavaScript için kullanılabilir hale getiren bir Access-Control-Expose-Headers
üst bilgi içerir.
Öğretici uygulamamızın istemci kimliği üst bilgisine erişebilmesi için CORS ara sunucusu kolayca yüklenebilir. İlk olarak, henüz yüklemediyseniz Node.js'yi yükleyin. Ardından komut penceresinde aşağıdaki komutu yürütün:
npm install -g cors-proxy-server
Sonra, HTML dosyasındaki Bing Web Araması uç noktasını şöyle değiştirin:
http://localhost:9090/https://api.cognitive.microsoft.com/bing/v7.0/search
Son olarak, aşağıdaki komutla CORS ara sunucusunu başlatın:
cors-proxy-server
Öğretici uygulamasını kullanırken komut penceresini açık bırakın; pencere kapatılırsa ara sunucu durdurulur. Arama sonuçlarının altındaki genişletilebilir HTTP Üst Bilgileri bölümünde artık X-MSEdge-ClientID
üst bilgisini (diğerleriyle birlikte) görebilir ve bunun her istekte aynı olduğunu doğrulayabilirsiniz.