يکي از مشکلاتي که در هنگام استفاده از وب سرويس ها ممکن است به آن بر بخوريد، حجم زياد اطلاعاتي است که بايد رد و بدل شود و گهگاه کاربر را در استفاده از متدهاي يک WebService، دچار مشکل مي کند. از آنجايي که XML زبان خلاصه گويي نيست و براي بيان اطلاعات Tag هاي فراوان دارد، حجم اطلاعات رد و بدل شده زياد است.
در اين مقاله راه حلي را براي ZIP کردن اطلاعات قبل از ارسال و باز کردن آنها پس از دريافت را شرح مي دهيم. اميد است که مورد استفاده قرار گيرد.
پيش نياز ها
اولين سؤالي که ممکن است پيش آيد اين است که از کدام الگوريتم براي ZIP کردن استفاده شود؟ با توجه به اينکه C# هيچ کلاسي براي اين کار ندارد مي توانيد از توابع و کتابخانه هاي آماده، استفاده کنيد. يکي از کتابخانه هاي معروف #ZipLib است که به صورت OpenSource موجود است و مي توانيد آن از آدرس زير Download کنيد.
http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx
در راه حل ارائه شده ، لازم نيست که تمام پيام فشرده شود و فقط محتويات پيام را فشرده شده و ارسال مي شود و پس از دريافت، فقط محتويات پيام باز مي شوند.
پياده سازي
دو کلاس بايد پياده سازي شوند، يکي بايد SoapExtention را توسعه دهد و کلاس ديگر SoapExtensionAttribute.
CompressionExtension
در کلاس CompressionExtension دو مرحله وجود دارد. يکي AfterSerialize و ديگر BeforeDeserialize. در AfterSerialize اطلاعات Zip مي شوند و اطلاعات فشرده شده در پيام قرار داده شده و ارسال مي شود. در مرحله BeforeDeserialize برعکس مرحله بالا انجام مي شود، بدين صورت که اطلاعات بررسي شده، UnZip مي شوند و اطلاعات بازشده در پيام قرار داده مي شود تا آماده استفاده باشد.
وقتي اطلاعات موجود در پيام فشرده مي شود، اطلاعات به آرايه اي از اطلاعات دودويي تبديل مي شود. امکان نمايش اطلاعات دودويي در در پيام ارسالي ممکن نيست، و بايد اطلاعات را با الگوريتم BASE64 تبديل کنيم.
using System;
using System.IO;
using System.Text ;
using System.Web.Services;
using System.Web.Services.Protocols ;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.GZip;
using System.Xml ;
namespace Radcom
{
///<summary>
/// Summary description for ConpressionExtension.
///</summary>
publicclass CompressionExtension : System.Web.Services.Protocols.SoapExtension
{
Stream oldStream;
Stream newStream;
publicoverrideobject GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return attribute;
}
// Get the Type
publicoverrideobject GetInitializer(Type t)
{
returntypeof(CompressionExtension);
}
// Get the CompressionExtensionAttribute
publicoverridevoid Initialize(object initializer)
{
CompressionExtensionAttribute attribute = (CompressionExtensionAttribute) initializer;
return;
}
// Process the SOAP Message
publicoverridevoid ProcessMessage(SoapMessage message)
{
// Check for the various SOAP Message Stages
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
// ZIP the contents of the SOAP Body after it has
// been serialized
Zip();
break;
case SoapMessageStage.BeforeDeserialize:
// Unzip the contents of the SOAP Body before it is
// deserialized
Unzip();
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
thrownew Exception("invalid stage");
}
1 }
// Gives us the ability to get hold of the RAW SOAP message
publicoverride Stream ChainStream( Stream stream )
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
// Utility method to copy streams
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
// Zip the SOAP Body
privatevoid Zip()
{
newStream.Position = 0;
// Zip the SOAP Body
newStream = ZipSoap(newStream);
// Copy the streams
Copy(newStream, oldStream);
}
// The actual ZIP method
privatebyte[] Zip(string stringToZip)
{
byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToZip);
MemoryStream ms = new MemoryStream();
// Check the #ziplib docs for more information
ZipOutputStream zipOut = new ZipOutputStream( ms ) ;
ZipEntry ZipEntry = new ZipEntry("ZippedFile");
zipOut.PutNextEntry(ZipEntry);
zipOut.SetLevel(9);
zipOut.Write(inputByteArray, 0 , inputByteArray.Length ) ;
zipOut.Finish();
zipOut.Close();
// Return the zipped contents
return ms.ToArray();
}
// Select and Zip the appropriate parts of the SOAP message
public MemoryStream ZipSoap(Stream streamToZip)
{
streamToZip.Position = 0;
// Load a XML Reader
XmlTextReader reader = new XmlTextReader(streamToZip);
XmlDocument dom = new XmlDocument();
dom.Load(reader);
// Load a NamespaceManager to enable XPath selection
XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
// Select the contents within the method defined in the SOAP body
node = node.FirstChild.FirstChild;
// Check if there are any nodes selected
while( node != null )
{
if( node.InnerXml.Length > 0 )
{
// Zip the data
byte[] outData = Zip(node.InnerXml);
// Convert it to Base64 for transfer over the internet
node.InnerXml = Convert.ToBase64String(outData) ;
}
// Move to the next parameter
node = node.NextSibling ;
}
MemoryStream ms = new MemoryStream();
// Save the updated data
dom.Save(ms);
ms.Position = 0;
return ms;
}
// Unzip the SOAP Body
privatevoid Unzip()
{
MemoryStream unzipedStream = new MemoryStream();
TextReader reader = new StreamReader(oldStream);
TextWriter writer = new StreamWriter(unzipedStream);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
// Unzip the SOAP Body
unzipedStream = UnzipSoap(unzipedStream);
// Copy the streams
Copy(unzipedStream, newStream);
newStream.Position = 0;
}
// Actual Unzip logic
privatebyte[] Unzip(string stringToUnzip)
{
// Decode the Base64 encoding
byte[] inputByteArray = Convert.FromBase64String( stringToUnzip ) ;
MemoryStream ms = new MemoryStream(inputByteArray) ;
MemoryStream ret = new MemoryStream();
// Refer to #ziplib documentation for more info on this
ZipInputStream zipIn = new ZipInputStream(ms);
ZipEntry theEntry = zipIn.GetNextEntry();
Byte[] buffer = new Byte[2048] ;
int size = 2048;
while (true)
{
size = zipIn.Read(buffer, 0, buffer.Length);
if (size > 0)
{
ret.Write(buffer, 0, size);
}
else
{
break;
}
}
return ret.ToArray();
}
// Unzip the SOAP Body
public MemoryStream UnzipSoap(Stream streamToUnzip)
{
streamToUnzip.Position = 0;
// Load a XmlReader
XmlTextReader reader = new XmlTextReader(streamToUnzip);
XmlDocument dom = new XmlDocument();
dom.Load(reader);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// Select the SOAP Body node
XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
node = node.FirstChild.FirstChild;
// Check if node exists
while( node != null )
{
if( node.InnerXml.Length >0 )
{
// Send the node's contents to be unziped
byte[] outData = Unzip(node.InnerXml);
string sTmp = Encoding.UTF8.GetString(outData);
node.InnerXml = sTmp;
}
// Move to the next parameter
node = node.NextSibling ;
}
MemoryStream ms = new MemoryStream();
dom.Save(ms);
ms.Position = 0;
return ms;
}
}
}
اين کلاس به شما اجازه مي دهد تا توابعي که از خاصيت فشرده سازي استفاده مي کنند را از ديگر توابع متمايز کنيد. اين کلاس از SoapExtensionAttribute ارث بري مي کند و مشخصه هاي ExtensionType و Priority را تغيير مي دهد.
using System; using System.Web.Services; using System.Web.Services.Protocols; namespace Radcom { /// <summary> /// Summary description for CompressionExtensionAttribute. /// </summary> // Make the Attribute only Applicable to Methods [AttributeUsage(AttributeTargets.Method)] public class CompressionExtensionAttribute : System.Web.Services.Protocols.SoapExtensionAttribute { private int priority; // Override the base class properties public override Type ExtensionType { get { return typeof(CompressionExtension); } } public override int Priority { get { return priority; } set { priority = value; } } } } |
نحوه استفاده
پس از کامپايل کردن، کتابخانه شما آماده است. براي استفاده در بخش سرويس دهنده، کافي است Namespace ي که براي پروژه خود درنظرگرفته ايد، در قسمت using بياوريد و بعد از تعريف تابع موردنظر خود (پس از [WebMethod]) عبارت [CompressionExtension] را اضافه کنيد و تابع موردنظر خود را به صورت عادي و معمول بنويسيد.
البته بديهي است که بايد DLL هاي مربوطه (SharpZip , CompressionExtension) را نيز در اختيار داشته باشيد تا بتوانيد از کتابخانه هاي توسعه يافته استفاده کنيد.
نکته ي مهم ديگر اينکه وقتي يک SOAP Call براي يک Web Method به وجود آيد، توابع توسعه يافته فعال شده و کار مي کنند و اگر براي مراجعه به توابع از HTTP GET / POST استفاده کنيد، توابع شما فعال نخواهند شد. براي مثال اگر شما از تست هاي استانداردي که ASP.NET براي تست وب سرويس در اختيار شما مي گذارد استفاده کنيد، نتيجه فشرده سازي را نخواهيد ديد.
<%@ WebService Language="C#" Class="MyService" %> using System.Web.Services; using MasterCSharp.WebServices ; using System.Data ; using System.Data.SqlClient ; public class MyService { [WebMethod] [CompressionExtension] public string MyMethod() { // Replace with the connection string to connect to your database DataSet ds = new DataSet(); /* Fill Dataset */ return ds.GetXml() ; } } |
براي استفاده از توابع در قسمت سرويس گيرنده ، پس از افزودن وب سرويس به عنوان Web Reference بايد DLL هاي موجود را به پروژه خود بيفزاييد (هر دو DLL) و سپس در فايل reference.cs در webreference مربوطه قبل از هر تابع عبارت [CompressionExtension] را بيفزاييد، چون به طور استاندارد وقتي مرجع را به پروژه خود مي افزاييد، اين مورد وجود ندارد.
using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; using Radcom; [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Web.Services.WebServiceBindingAttribute (Name="MyServiceSoap", Namespace="http://tempuri.org/")] public class MyService : System.Web.Services.Protocols.SoapHttpClientProtocol { public MyService() { this.Url = "http://localhost/TestPages/Service.asmx"; } /// <remarks/> // Add our Custom SOAP Extension [CompressionExtension] [System.Web.Services.Protocols.SoapDocumentMethodAttribute ("http://tempuri.org/MyMethod", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public string MyMethod() { object[] results = this.Invoke("MyMethod", new object[0]); return ((string)(results[0])); } /// <remarks/> public System.IAsyncResult BeginMyMethod(System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("MyMethod", new object[0], callback, asyncState); } /// <remarks/> public string EndMyMethod(System.IAsyncResult asyncResult) { object[] results = this.EndInvoke(asyncResult); return ((string)(results[0])); } } |
منبع:www.radcom.ir/خ