Navigace

Hlavnφ menu

 

SOAP extenze pro komprimaci komunikace

V p°edchozφm Φlßnku jsem popsal zßkladnφ rysy SOAP extenzφ a jejich v²znam. TakΘ jsme spoleΦn∞ vytvo°ili SOAP extenzi, kterß sledovala komunikaci mezi klientem a serverem. U₧iteΦnost SOAP extenzφ ovÜem jeÜt∞ vφce vynikne, pokud nechceme SOAP zprßvy jen pasivn∞ sledovat, ale chceme je nap°φklad komprimovat nebo Üifrovat pro zv²Üenφ bezpeΦnosti a snφ₧enφ nßrok∙ na komunikaci mezi klientem a serverem.

Zßkladnφ po₧adavky na SOAP extenzi komprimujφcφ data

Komprimace SOAP zprßv je zvlßÜt∞ v²hodnß v p°φpad∞ v²m∞ny velk²ch datov²ch sad (dataset∙) mezi klientem a serverem, ale SOAP extenzi je samoz°ejm∞ mo₧nΘ pou₧φt s libovolnou slu₧bou. NaÜe SOAP extenze by m∞la spl≥ovat nßsledujφcφ po₧adavky:

  • VeÜkerß komunikace mezi klientem a serverem je komprimovßna.
  • V SOAP zprßv∞ je ulo₧ena informace, ₧e se jednß o komprimovanou zprßvu.
  • Pokud je zaslßna nekomprimovanß zprßva, SOAP extenze se nesna₧φ zprßvu dekomprimovat.

Z po₧adavk∙ plyne, ₧e je nutnΘ komprimovat a dekomprimovat SOAP zprßvy na serveru i na klientovi, proto musφ b²t SOAP extenze nainstalovßna na serveru i u klienta (konzumenta) WWW slu₧by. To je rozdφl ve srovnßnφ se SOAP extenzφ pro sledovßnφ komunikace mezi klientem a serverem, kterß byla nainstalovßna pouze na serveru.

Po°adφ a v²znam fßzφ p°i volßnφ metody ProcessMessage se u klienta liÜφ od po°adφ a v²znamu fßzφ na serveru. Jak jsme ji₧ °ekli minule, v jakΘ fßzi se prßv∞ nachßzφme, zjistφme z vlastnosti Stage t°φdy SoapMessage, jejφ₧ instance je jedin²m argumentem metody ProcessMessage. Vlastnost Stage obsahuje hodnotu z enumerace SoapMessageStage, jejφ₧ Φleny a jejich v²znam u klienta si nynφ popφÜeme p°esn∞ tak, jak jsme to ud∞lali u serveru.

Hodnota enumeracePopis
BeforeDeserializeByla p°ijata odpov∞∩ ze serveru, ale jeÜt∞ nebyla deserializovßna. Toto je fßze, ve kterΘ musφme zprßvu dekomprimovat, aby mohl .NET Framework pracovat se standardnφ SOAP zprßvou.
AfterDeserializeOdpov∞∩ ze serveru byla deserializovßna, to znamenß, ₧e z instancφ XSD typ∙ v SOAP zprßv∞ byly vytvo°eny korespondujφcφ instance typ∙ .NET Frameworku.
BeforeSerializePo₧adavek na server jeÜt∞ nebyl serializovßn.
AfterSerializePo₧adavek na server byl ·sp∞Ün∞ vytvo°en a serializovßn. V tΘto fßzi provedeme komprimaci SOAP zprßvy.

Pro komprimaci pou₧ijeme voln∞ Üi°itelnou knihovnu SharpZipLib. Zkomprimovanß data jsou do SOAP zprßvy zapsßna v k≤dovßnφ Base64. SOAP extenze bude do ka₧dΘ zprßvy p°i komprimaci p°idßvat hlaviΦku (SOAP Header) s nßzvem IsCompressed, podle kterΘ lze spolehliv∞ poznat, ₧e se jednß o komprimovanou zprßvu. Komprimovßno smφ tedy b²t pouze t∞lo zprßvy (obsah elementu soap:Body), ale ne hlaviΦky zprßvy (soap:Header elementy)!

Komprimace a dekomprimace pomocφ knihovny SharpZipLib vyboΦuje z tΘmatu Φlßnku, zßjemci si mohou dokumentaci ke knihovn∞ stßhnout ze serveru SourceForge.net.

Vytvß°φme extenzi

Proto₧e je k≤d SOAP extenze pom∞rn∞ rozsßhl², budu v Φlßnku popisovat jen nejd∙le₧it∞jÜφ t°φdy a metody û nejvφce se nauΦφte prostudovßnφm p°ilo₧enΘho zdrojovΘho k≤du. Pokud se psanφm SOAP extenzφ zab²vat nechcete a zajφmß vßs jen pou₧itφ SOAP extenze ve vaÜich projektech, m∙₧ete pou₧φt v²Üe zkompilovanou verzi SOAP extenze, kterou takΘ naleznete ve v²Üe zmφn∞nΘm archivu.

P°i vytvß°enφ SOAP extenze musφme nejd°φve vytvo°it pomocnΘ funkce pro komprimaci. Abychom nezat∞₧ovali SOAP extenzi implementacφ algoritmu pro komprimaci a dekomprimaci, vytvo°φme pomocnou t°φdu s nßzvem ZipUtilities, kterß bude obsahovat dv∞ ve°ejnΘ metody. Metoda ZipSoapBody komprimuje obsah p°edanΘho Streamu, metoda UnzipSoapBody analogicky dekomprimuje:

public virtual Stream ZipSoapBody (Stream inputStream)
  {
    XmlTextReader myReader = new XmlTextReader(inputStream);
    XmlDocument myDocument = new XmlDocument();
    myDocument.Load(myReader);
    XmlNamespaceManager myManager = new XmlNamespaceManager(myDocument.NameTable);
    myManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
    XmlNode bodyNode = myDocument.SelectSingleNode(@"//soap:Body",myManager);
    String originXML = bodyNode.InnerXml;
    // Nic ke komprimaci
    if(originXML.Length == 0)
      {
        return inputStream;
      }
    XmlNode headerNode = myDocument.SelectSingleNode(@"//soap:Header", myManager);
    if (headerNode == null)
      {
        headerNode = myDocument.CreateElement("soap:Header", "http://schemas.xmlsoap.org/soap/envelope/");
        bodyNode.ParentNode.InsertBefore(headerNode,bodyNode);
      }
    XmlElement compressedHeader = myDocument.CreateElement("IsCompressed");
    headerNode.AppendChild(compressedHeader);
    string zipXML = this.zipString(originXML);
    bodyNode.InnerXml = zipXML;
    MemoryStream retStream = new MemoryStream();
    myDocument.Save(retStream);
    return retStream;
  }

Metod∞ je p°edßn Stream obsahujφcφ SOAP zprßvu, je₧ mß b²t zkomprimovßna. Nejprve je vytvo°en XmlTextReader, do kterΘho je naΦten p°edan² Stream, a potΘ je z XmlTextReaderu vytvo°en XmlDocument. V XML dokumentu je vyhledßn element soap:Body. Pokud je prßzdn², je vrßcen p∙vodnφ Stream a k ₧ßdnΘ komprimaci nedojde. Pokud prßzdn² nenφ, p°idßme "hlaviΦku" IsCompressed a v privßtnφ metod∞ ZipString provedeme samotnou komprimaci. Obsah (p°esn∞ji °eΦeno hodnotu vlastnosti InnerXml) elementu soap:Body nahradφme zkomprimovan²m °et∞zcem. Zm∞n∞n² obsah XML dokumentu ulo₧φme do novΘho Streamu, kter² z metody vrßtφme.

Ve t°φd∞ ZipUtilities je takΘ ve°ejnß vlastnost ZipLevel, kterß urΦuje stupe≥ komprimace. M∙₧e nab²vat hodnot od jednΘ do devφti. Bohu₧el, knihovna SharpZipLib obsahovala v dob∞ vzniku tohoto Φlßnku chybu, kterß se projevovala p°i komprimaci velkΘho mno₧stvφ dat a souΦasn∞ p°i ·rovni komprimace menÜφ nebo rovnΘ p∞ti û proto vßm doporuΦuji tuto vlastnost nevyu₧φvat a nechat standardn∞ p°ednastavenou ·rove≥ ädev∞tô.

Po napsßnφ pomocnΘ t°φdy vytvo°φme SOAP extenzi. Nejprve p°epφÜeme ob∞ verze metody pro prvotnφ inicializaci SOAP extenze:

public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
  {
    return attribute;
  }

public override object GetInitializer(Type t)
  {
    return new ZipExtensionAttribute();
  }

Prvnφ verze metody je volßna v p°φpad∞ zaregistrovßnφ SOAP extenze k WWW slu₧b∞ pomocφ atributu ZipExtensionAttribute. Metoda pouze p°φsluÜn² atribut vrßtφ. Druhß verze je volßna v p°φpad∞, ₧e SOAP extenze byla zaregistrovßna k WWW slu₧b∞ v konfiguraΦnφm souboru web.config. Metoda vytvo°φ novou instanci atributu a vrßtφ ji. Ob∞ metody vracφ instanci t°φdy ZipExtensionAttribute, kterß je potomkem t°φdy SoapExtensionAttribute a kterß je pou₧ita nejen k asociovßnφ naÜφ SOAP extenze s WWW metodou, ale takΘ navφc obsahuje vlastnost ZipLevel, je₧ urΦuje ·rove≥ komprimace. Vytvß°enφ atribut∙ je trivißlnφ a bylo podrobn∞ popsßno v p°edchozφm Φlßnku.

V p°epsanΘ metod∞ ProcessMessage jsou v p°φsluÜn²ch fßzφch volßny pomocnΘ privßtnφ funkce pro komprimaci a dekomprimaci, kterΘ pou₧φvajφ t°φdu ZipUtilities. Instance t°φdy ZipUtilities je vytvo°ena v metod∞ Initialize p°i ka₧dΘm po₧adavku na WWW slu₧bu. Ve fßzi AfterSerialize je SOAP zprßva zkomprimovßna, ve fßzi BeforeDeserialize je zprßva dekomprimovßna.

Tφm je SOAP extenze pro komprimaci hotova. Jak se pou₧φvajφ SOAP extenze na serverovΘ stran∞, bylo vysv∞tleno v p°edchozφm Φlßnku, nynφ se zam∞°φme na pou₧itφ SOAP extenzφ u klienta. Na klientovi musφ b²t nainstalovßna knihovna se SOAP extenzφ i knihovna SharpZipLib. Nejprve musφte vytvo°it proxy t°φdu WWW slu₧by. NejjednoduÜÜφ je p°idßnφ reference na WWW slu₧bu ve VS.NET, p°φpadn∞ m∙₧ete proxy vytvo°it manußln∞ pomocφ programu wsdl.exe z .Net Framework SDK.

wsdl /out:MyServicer.cs <URL popisu slu₧by>

P°epφnaΦ out urΦuje jmΘno v²slednΘ proxy t°φdy, parametr <URL popisu slu₧by> musφ obsahovat cestu k WSDL popisu vaÜφ slu₧by. Do vygenerovanΘ proxy t°φdy p°idßte k metodßm atribut Interval.SoapExtensions ZipExtensionAttribute a do konkrΘtnφho projektu (ASP.NET, Win Forms) p°idßte referenci na knihovnu IntervalExtensions. Upozor≥uji, ₧e po aktualizaci reference na WWW slu₧bu se vÜechny ruΦn∞ dopln∞nΘ informace z proxy t°φdy ztratφ, musφte proto atributem Interval.SoapExtensions ZipExtensionAttribute dekorovat metody znovu.

[Interval.SoapExtensions.ZipExtension]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/GetData", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public System.Data.DataSet GetData()
{
  object[] results = this.Invoke("GetData", new object[0]);
  return ((System.Data.DataSet)(results[0]));
}

Extenzi je samoz°ejm∞ mo₧nΘ jeÜt∞ vylepÜovat. Kdy₧ p°ijde na server nekomprimovan² SOAP po₧adavek, nebude komprimovßna ani SOAP odpov∞∩. T°φdu ZipUtilities by by mo₧nΘ ud∞lat zcela bezstavovou (bez vlastnostφ) a realizovat ji pomocφ nßvrhovΘho vzoru Singleton. To u₧ jsou ale cviΦenφ vhodnß pro zaujatΘho Φtenß°e.

Stein, RenΘ (24.7. 2003)
analytik a v²vojß° spoleΦnosti DIGI-TRADE