Náš zájem je dnes stejně jako v díle předchozím zamířen na aplikace pracující v síti. Dnes se ale podíváme na to jakým způsobem je možné na platformě .NET implementovat síťové aplikace, využívající architekturu požadavek/odpověď.
Architektura požadavek/odpověď v .NET
I přestože je přístup k síťovým zdrojům pomocí třídy WebClient, kterou jsme si představovali v minulém díle, velmi jednoduchý, často při implementaci síťových aplikací narazíme na situace, kde pro nás bude její vysoká míra abstrakce omezující. V takovýchto případech „sáhneme“ po dvojici tříd WebRequest a WebResponse ze jmenného prostoru System.Net. Tyto třídy pro nás představují cestu k implementaci aplikací na základě přístupu požadavek/odpověď, kde je na základě URI proveden požadavek na vzdálený zdroj a ten je nám, pokud vše dobře zafunguje, v podobě odpovědi navrácen.
Implementace zmíněného přístupu pomocí tříd WebRequest a WebResponse je založen na principu datových proudů a využívání abstraktních tříd či rozhraní. Obě uvedené třídy jsou třídami abstraktními, tudíž není možné vytvořit jejich instance a jsou jen obecnými bázovými třídami, z nichž jsou odvozeny implementace pro konkrétní síťové protokoly. Cestou k vytvoření objektu představující požadavek na konkrétní vzdálený zdroj je použití statické tovární metody Create třídy WebRequest .
Po tomto vytvoření jsme schopni na vzniklém objektu pomocí jeho metody získat objekt představující odpověď. Pokud v naší aplikaci nepotřebujeme využívat vlastností jednotlivých protokolů (např. HTTP) můžeme pracovat pouze s rozhraním tříd WebRequest a WebResponse a tím tedy pracovat pouze na obecné úrovní požadavek/odpověď. Standardně jsou v .NET frameworku verze 1.1 implementovány třídy ovladačů pro protokoly HTTP, HTTPS a FILE. V .NET frameworku verze 2.0 ještě přibyla podpora pro protokol FTP.
Jak by mohl vypadat jednoduchý příklad, který pro přístup ke vzdálenému zdroji využívá tříd WebRequest a WebResponse ukazuje následující příklad.
//nacteni URI, na ktery bude proveden pozadavek
Console.Write("Zadejte URI (napr. ) : ");
string uri = Console.ReadLine();
//vytvoreni instance pozadavku
WebRequest request = WebRequest .Create(uri);
WebResponse response = null;
StreamReader reader = null;
try
{
//vyslani pozadavku a ziskani odpovedi
response = request.GetResponse();
//ziskani datoveho proudu odpovedi
Stream strm = response.GetResponseStream();
reader = new StreamReader(strm);
//nacteni textu odpovedi
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch(WebException ex)
{
Console.WriteLine("Doslo k vyjimce : {0}", ex.ToString());
}
finally
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
}
Tovární metoda Create na základě identifikátoru protokolu v URI vrací objekt požadavku pro daný protokol, ke kterému je standardně přistupováno přes rozhraní abstraktní třídy WebRequest . Vytvořením objektu požadavku není ještě skutečný síťový požadavek odeslán. Děje se tomu až v čase získávání objektu odpovědi, což učiníme zavoláním metody GetResponse na objektu požadavku. Pro získání datového proudu obdržené odpovědi využijeme metodu GetResponseStream a následně jej nějakým způsobem zpracujeme. Po tom co získanou odpověď zpracujeme, tak nikdy nesmíme zapomenout na uzavření spojení, což zařídíme metodou Close na objektu odpovědi.
Specifické vlastnosti protokolu HTTP
Z důvodu zachování určité míry abstrakce se často pracuje pouze na úrovní použití rozhraní základních tříd WebRequest a WebResponse. Metoda Create vrací odkaz na obecný typ požadavku WebRequest a metoda GetResponse zase na obecný typ odpovědi WebResponse. Tím je dosažena nezávislost na síťovém protokolu ve zdrojovém kódu.
To jací specifičtí potomci tříd WebRequest a WebResponse jsou ve skutečnosti používány je závislé pouze na použitém URI, který je předán metodě Create. Ve skutečnosti tovární metoda Create v případě, že je jako identifikátor protokolu v URI uvedeno http://, vrátí instanci třídy HttpWebRequest a metoda GetResponse vrací instanci třídy HttpWebResponse.
V případě použití identifikátoru protokolu file:// jsou zase vráceny specializované instance FileWebRequest a FileWebResponse. Takže pokud chceme využít specifické vlastnosti protokolu HTTP, využijeme těchto specializovaných potomků. Toho jednoduše docílíme pomocí přetypování směrem dolů, jak ukazuje následující příklad.
//nacteni URI, na ktery bude proveden pozadavek
Console.Write("Zadejte adresu pro HTTP pozadavek (napr. www.zive.cz) : ");
string uri = "http://" + Console.ReadLine();
//vytvoreni instance pozadavku
HttpWebRequest request = (HttpWebRequest ) WebRequest .Create(uri);
//nastaveni specificke vlastnosti HTTP pozadavku
request.UserAgent = "Pokusny klient";
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
//precteni specifickych vlastnosti HTTP odpovedi
Console.WriteLine("Last modified : {0}", response.LastModified);
Console.WriteLine("Status code : {0}", response.StatusCode);
}
finally
{
if (response != null)
{
response.Close();
}
}
Tím, že jsme provedli přetypování na specializované implementace tříd WebRequest /WebResponse jsme získali možnost pomocí jejich instančních vlastností specifikovat (v případě požadavku) a číst (v případě odpovědi) specifické informace obsažené v záhlaví. Takže v ukázkovém zdrojovém kódu jsme například požadavku nastavili vlastnost USER AGENT sloužící k podání informací o použitém prohlížeči a z objektu odpovědi jsme přečetli datum poslední modifikace a kód stavu.
K záhlaví požadavku čí odpovědi lze přistupovat i na obecné úrovni tříd WebRequest a WebResponse a to pomocí jejich instanční vlastnosti Headers, která je typu kolekce WebHeaderCollection. Pomocí prvků této kolekce jsme schopni přistupovat i k těm informací v záhlaví, které nejsou k dispozici pomocí API na specializovaných potomcích. Samozřejmě vlastnost Headers můžete využít nejen u obecných tříd WebRequest a WebResponse, ale i u jejich odvozených tříd (např. HttpWebRequest ).
//nacteni URI, na ktery bude proveden pozadavek
Console.Write("Zadejte URI (napr. ) : ");
string uri = Console.ReadLine();
//vytvoreni instance pozadavku
WebRequest request = WebRequest .Create(uri);
WebResponse response = null;
try
{
//vyslani pozadavku a ziskani odpovedi
response = request.GetResponse();
for (int i = 0; i < response.Headers.Count; i++)
{
string headerKey = response.Headers.GetKey(i);
string headerValue = response.Headers[i];
Console.WriteLine("{0} - {1}", headerKey, headerValue);
}
}
catch(WebException ex)
{
Console.WriteLine("Doslo k vyjimce : {0}", ex.ToString());
}
finally
{
if (response != null)
{
response.Close();
}
}
Zdrojové kódy příkladů ke článku jsou ke stažení zde.