Vývoj sofistikovaných WWW služeb v .Net Frameworku

19. května 2004

Net Framework od verze 1.0 dovoluje vyvíjet WWW služby, které pro transport zpráv používají HTTP. WWW služby založené na HTTP protokolu se tak rozšířily, že u některých méně znalých to může mylně vzbuzovat domnění, že transport SOAP zpráv přes HTTP protokol je pro WWW služby něčím přirozeným nebo dokonce povinným. SOAP je ale protokol zcela nezávislý na používaném transportním protokolu a s komponentou Web Services Enhancements 2.0 můžeme nyní vytvářet také WWW služby používající například přenosový protokol TCP/IP (soap.tcp).

Web Services Enhancements 2.0 technology preview (dále jen WSE) si můžete stáhnout z webu Microsoftu. WSE je zkušební implementací mnoha specifikací největších hráčů v oblasti WWW služeb (Microsoft, IBM). Nové specifikace řeší hlavní nedostatky současných WWW služeb, například neexistující standardy pro zabezpečení nebo adresování SOAP zpráv. V dnešním článku se budeme ale zabývat pouze novým API pro výměnu SOAP zpráv.

Výhody a nevýhody WSE

Ve WSE naleznete vysokoúrovňové API a nízkoúrovňové API pro posílání SOAP zpráv. I když je vysokoúrovňové API pochopitelně jednodušší, tento článek se zaměřuje na nízkoúrovňové API, abyste lépe pochopili infrastrukturní pozadí nového náhledu na WWW služby, jež přichází s WSE. Začněme špatnou zprávou. Vývoj WWW služeb ve WSE je složitější než vývoj běžných služeb odvozených z třídy System.Web.Services.WebService. U tříd dědících z WebService stačí dekorovat metody určené ke zveřejnění atributem WebMethod a běhové prostředí za nás vygeneruje popis služby (WSDL) i se postará o serializaci a deserializaci SOAP zpráv z/do aplikačních objektů. WSE API vyžaduje, aby vývojář ovládal alespoň základy SOAP protokolu, protože SOAP zprávy vytváříme a zpracováváme sami. Myslím ale, že WWW služby, které můžeme hostovat například ve Windows službě a jejichž komunikace probíhá po TCP nebo HTTP protokolu, jsou dostatečnou odměnou za náš počáteční znalostní vklad. WSE API také vytěsňuje z našich postupů zažitý model komunikace, při kterém jsou neodvolatelně vymezeny role klienta WWW služby a WWW služby samotné (Request/Response, klient/server).

Hlavní třídy WSE pro výměnu SOAP zpráv

Nízkoúrovňové API představují hlavně následující třídy, jejichž úlohu si nyní popíšeme:

  • SoapEnvelope – třída SoapEnvelope je objektovou reprezentací SOAP zprávy. Protože dědí z třídy XmlDocument, můžeme používat všechny metody pro zpracování XML, jež jsou součástí DOM specifikace. V této třídě jsou důležité metody CreateHeader a CreateBody, které vytvoří a vrátí základní XML elementy každé SOAP zprávy (soap:Header, soap:Body).
  • SoapSender – třída SoapSender je určena k odesílání SOAP zpráv. Její konstruktor přijímá URI, na které chceme SOAP zprávy posílat. Metoda Send odešle argument typu SoapEnvelope na zadané URI.
  • SoapReceiver – z této třídy musí být odvozen náš konkrétní příjemce (zpracovatel) SOAP zpráv. V potomkovi vždy musíme přepsat metodu Receive, jejímž argumentem je obdržená SOAP zpráva.
  • SoapReceivers – v třídě SoapReceivers se metodou Add registrují příjemci zpráv. Každý příjemce musí být zaregistrován s jedinečným URI, aby WSE dokázalo směrovat přijaté zprávy na správného příjemce.

Ukázka WSE služeb

S těmito znalostmi můžeme vytvořit dvě demonstrační konzolové aplikace, v nichž budou hostovány WWW služby komunikující přes TCP protokol. Konzolové aplikace v ukázce používám z důvodu jejich snadného nasazení a testování. V aplikaci vyvíjené pro zákazníka byste měli vždy WWW služby hostovat raději ve Windows službě. V obou aplikacích musíte přidat referenci na assembly Microsoft.Web.Services z WSE. První aplikace (RequestApp) iniciuje komunikaci s druhou aplikací (ResponseApp) zasláním SOAP zprávy. Aplikace ResponseApp zprávu přijme, zjistí, co ji bylo zasláno a zašle aplikaci RequestApp SOAP zprávu s odpovědí. I když jsem aplikacím přidělil konvenční názvy RequestApp a ResponseApp, nejde o tradiční Request/Response model, protože obě aplikace jsou při příjmu a odesílání zpráv zcela rovnocenné. Použitými názvy jsem chtěl jen naznačit, která aplikace v našem scénáři zahajuje komunikaci (RequestApp) a která aplikace generuje odpověď (ResponseApp). Zdrojové kódy si můžete stáhnout .

Nejdříve si ukážeme třídu ResponseReceiver z aplikace RequestApp. ResponseReceiver vytváří a odesílá SOAP zprávy s požadavkem a zároveň je příjemcem další SOAP zprávy s odpovědí z ResponseApp.

public class ResponseReceiver : SoapReceiver
{
   public const string SERVICEURI = „soap.tcp://RSTEIN2:1001/TESTSERVICEREQUEST“;
   public ResponseReceiver() : base()
   {
   }
   protected override void Receive(Microsoft.Web.Services.SoapEnvelope envelope)
   {
     Console.WriteLine(envelope.InnerXml);
     XmlNamespaceManager manager = new XmlNamespaceManager(envelope.NameTable);
     manager.AddNamespace(„ts“, „urn:TESTSERVICE“);
     string message = envelope.Body.SelectSingleNode(„//ts:result“, manager).InnerText;
     Console.WriteLine(message);
   }
   public void SendRequest()
   {
     SoapEnvelope newEnvelope = new SoapEnvelope();
     XmlElement soapBody = newEnvelope.CreateBody();
     soapBody.InnerXml = „<ts:testRequest xmlns:ts=’urn:TESTSERVICE‘><ts:test>Rene</ts:test></ts:testRequest>“;
     newEnvelope.Context.ReplyTo = new ReplyTo(new Uri(SERVICEURI));
     newEnvelope.Context.Action = „urn:TESTSERVICERESPONSE:RESPONSE“;
     SoapSender sender = new SoapSender(new Uri(„soap.tcp://RSTEIN2:1002/TESTSERVICERESPONSE“));
     sender.Send(newEnvelope);
   }
}

Třída ResponseReceiver dědí z třídy SoapReceiver, protože budeme přijímat odpověď na náš požadavek z aplikace ResponseApp. Konstanta SERVICEURI obsahuje URI, na kterém bude naslouchat instance třídy ResponseReceiver. URI je složeno z názvu protokolu, kterým je v našem případě soap.tcp protocol. soap.tcp je povinný název protokolu pro WWW služby založené na WSE modelu, které chtějí komunikovat přes TCP. Po protokolu následuje jméno počítače (RSTEIN2) a port (1001). Jméno počítače musíte v příkladu samozřejmě změnit tak, aby se shodovalo s názvem vašeho počítače, a zadaný port nesmí být obsazen jinou aplikací! Poslední část URI je tvořen řetězcem TESTSERVICEREQUEST, který jednoznačně odliší naší aplikací od ostatních WWW služeb, jež by mohly být na počítači spuštěny.Požadavek vytváří a odesílá metoda SendRequest. V metodě je vytvořen objekt SoapEnvelope s názvem newEnvelope, v němž je uložen požadavek, který bude zaslán aplikaci ResponseApp. Voláním metody CreateBody vytvoříme v SOAP zprávě povinný element soap:Body. Protože je naše zpráva velmi jednoduchá, zadáme obsah elementu soap:Body přímo do vlastnosti InnerXml. Důležitý je element test s mým jménem, protože ResponseApp, jak uvidíme, si jméno „přečte“ a vrátí jej v odpovědi. Vlastnost Context třídy SoapEnvelope je instancí třídy SoapContext a dovoluje nám mimo jiné nastavit, kam má server zaslat odpověď (vlastnost ReplyTo). My chceme zaslat odpověď na URI, na němž bude naslouchat ResponseReceiver. Dále dosadíme do vlastnosti Action URI, které jednoznačně identifikuje metodu WWW služby, jež má být v ResponseApp zavolána. V našem příkladu vlastnost Action nemá takový význam, protože aplikace ResponseApp podporuje pouze jednu metodu, ale ve složitějších aplikacích na jednom „naslouchajícím“ URI zpracovává SOAP zprávy více metod, ze kterých si musíme právě jednu vybrat. Nakonec vytvoříme objekt SoapSender, kterému do konstruktoru předáme URI WWW služby hostované v ResponseApp, a metodou Send vytvořený požadavek odešleme.

V chráněné metodě Receive přijmeme odpověď ze serveru. Celou odpověď (InnerXml) vypíšeme z výukových důvodu do konzole . Dále nalezneme pomocí metody SelectSingleNode element ts:result a jeho obsah opět vypíšeme do konzole. Již teď prozradím, že ResponseApp do elemetu ts:result ukládá řetězec Ahoj, k němuž přidá řetězec zaslaný v elementu ts:test požadavku. V našem případě se tedy musí vypsat „Ahoj Rene“.

Následující kód vytvoří instanci příjemce ResponseReceiver a přidá ji do kolekce SoapReceivers pod URI v konstantě ResponseReceiver.SERVICEURI. Teprve po zaregistrování jsou příjemci doručovány zprávy zaslané na dané URI! Nakonec je volána metoda SendRequest, která pošle SOAP zprávu WWW službě v aplikaci ResponseApp.

class StartApp
{
   static void Main(string[] args)
   {
     ResponseReceiver responseReceiver = new ResponseReceiver();
     SoapReceivers.Add(new Uri(ResponseReceiver.SERVICEURI), responseReceiver);
     responseReceiver.SendRequest();
     Console.ReadLine();
   }
}

Kód příjemce SOAP zpráv v ResponseApp se příliš neliší od již popsaného kódu příjemce v RequestApp.

public class RequestReceiver : SoapReceiver
{
   public const string SERVICEURI = „soap.tcp://RSTEIN2:1002/TESTSERVICERESPONSE“;
   public RequestReceiver()
   {
   }
   protected override void Receive(Microsoft.Web.Services.SoapEnvelope envelope)
   {
     Console.WriteLine(envelope.InnerXml);
     Console.WriteLine(envelope.Context.Action);
     XmlNamespaceManager manager = new XmlNamespaceManager(envelope.NameTable);
     manager.AddNamespace(„ts“, „urn:TESTSERVICE“);
     string message = envelope.Body.SelectSingleNode(„//ts:test“, manager).InnerText;
     SoapEnvelope response = CreateResponseMessage(message);
     response.Context.Action = new Action(„urn:TESTSERVICEREQUEST:PROCESSRESPONSE“);
     SoapSender sender = new SoapSender(envelope.Context.ReplyTo);
     sender.Send(response);
   }
   private SoapEnvelope CreateResponseMessage(string message)
   {
     SoapEnvelope newEnvelope = new SoapEnvelope();
     XmlElement soapBody = newEnvelope.CreateBody();
     soapBody.InnerXml = String.Format(„<ts:testRequestResponse xmlns:ts=’urn:TESTSERVICE‘><ts:result>Ahoj {0}</ts:result></ts:testRequestResponse>“, message);
     return newEnvelope;
  }
}

V metodě Receive třídy RequestReceiver opět vypíšeme celou SOAP zprávu (InnerXml) a také atribut Context.Action. Jak jsem již zmínil, na základě hodnoty atributu Action se WWW služba rozhoduje, jaká WWW metoda má být vyvolána. WWW služba ResponseApp zpracovává pouze jeden typ zprávy, takže atribut Action nepoužívá. Dále je zjišten obsah elementu ts:test (Rene), který je předán pomocné metodě CreateResponseMessage. Metoda CreateResponse vytvoří objekt SoapEnvelope s odpovědí (Ahoj Rene). SOAP zpráva s odpovědí je zaslána objektem SoapSender původnímu odesílateli, jehož URI je uložena v atributu Context.ReplyTo přijaté zprávy.

Kód pro registraci instance třídy RequestReceiver se od kódu pro registraci objektu ResponseReceiver liší pouze v URI, na němž bude WWW služba přijímat požadavky.

class StartApp
{
     static void Main(string[] args)
     {
     SoapReceiver requestReceiver = new RequestReceiver();
     SoapReceivers.Add(new Uri(RequestReceiver.SERVICEURI), requestReceiver);
     Console.ReadLine();
    }
}

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *