Во последниот период правев доста тестирања при работа со Веб Сервиси и пренос на податоци од серверот кадешто е Веб Сервисот до клиентот – самиот конзумер на Веб Сервисот.

Тестирањата ги правев со обид да се пренесе долга byte[] низа која ја надминува основната големина поставена при генерирање на bindings на самиот веб сервис.

Сценариото е следно:

- Клиентот повикува Веб Метод

- Целата логика е на страна на Веб Сервисот, така што на страна на веб сервисот се креира извештај кој треба да биде прикажан во PDF (под default) или друг формат, при што, веб сервисот враќа готов извештај во byte[] низа до клиентот

- На крај, клиентот ја користи таа byte[] низа за креирање на документот.

При обид да се земе byte[] низа вратена од Веб Сервисот

byte[] rep = serv.getReport(json);

првата грешка која можете да ја добиете е:

The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.

Како што грешката објаснува, MaxReceivedMessageSize property вредноста треба да биде зголемена.

Во Web.config под <system.serviceModel>, откако ќе додадете референца до веб сервисот, bindings и client елементи се креирани.

Треба да изгледа од прилика вака:

<system.serviceModel>
 <bindings>
  <basicHttpBinding>
    <binding name="Service1Soap" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
      allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
      messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
    <security mode="None">
     <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
     <message clientCredentialType="UserName" algorithmSuite="Default"/>
    </security>
   </binding>
  </basicHttpBinding>
 </bindings>
 <client>
  <endpoint address="http://hajan/mywebservapp/MyService.asmx" binding="basicHttpBinding" bindingConfiguration="Service1Soap" contract="HSServ.Service1Soap" name="Service1Soap"/>
 </client>
</system.serviceModel>

Најпрво, треба да обрнеме внимание на следниов елемент:


<binding name="Service1Soap" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">

Воглавно, ќе правиме промени на болдираните атрибути на binding елементот.

Бидејќи не бев сигурен колку голема може да биде byte[] низата во иднина, поставив максимална вредност од 2^31 - 1 или 2147483647 - најголема 32-битна вредност.

Следно, треба да обрнеме внимание на transferMode, чија вредност под default е поставена на Buffered.

- Bufferred значи дека и двете request/response пораки ќе бидат зачувани во buffer.

Други можни опции на transferMode се:

- Streamed - што значи и request и response пораките ќе бидат streamed, или

- StreamedRequest кадешто request пораката ќе биде streamed додека response пораката ќе биде buffered, или

- StreamedResponse кадешто request пораката ќе биде buffered додека response пораката streamed.

За тие што не знаат, Buffered значи трансферот ќе ја зачува целата порака во меморијата на buffer-от се додека трансферот заврши.
Streamed значи дека само хедерите на пораката ќе бидат во buffer, додека главниот дел на пораката (message body) ќе биде како stream.

Во нашиов случај, откако направивме промена на maxReceivedMessageSize од 65536 на 2147483647 – ако го оставиме transferMode="Buffered" ќе добиеме друга грешка:

For TransferMode.Buffered, MaxReceivedMessageSize and MaxBufferSize must be the same value. Parameter name: bindingElement

Да. Задолжително е и двете вредности на атрибутите MaxReceivedMessageSize и MaxBufferSize да имаат иста вредност, што е логично бидејќи MaxReceivedMessageSize (во овој момент) е поголема од MaxBufferSize, а ние очекуваме целата порака да биде во buffer.

Оттука, произлегуваат две опции:

a) Можеме да ја смениме големината на MaxBufferSize на 2147483647

b) Или пак да го направиме transferMode-от: Streamed, StreamedRequest или StreamedResponse.

Јас би препорачал да одиме со опција b бидејќи streamed трансферите може да ја зголемат скалабилноста на апликацијата т.е. приспособливоста на сервисот посебно при пренос на големи пораки, при што се елиминира потребата од големи buffer-и.

Во библиотеката на MSDN, го најдов следниов параграф во врска со донесувањето одлука дали да користиме buffered или streamed трансфери:

"The decision to use either buffered or streamed transfers is a local decision of the endpoint for HTTP transports. For HTTP transports, the transfer mode does not propagate across a connection, or to proxy servers or other intermediaries. Setting the transfer mode is not reflected in the description of the service contract. After generating a proxy to a service, you can (it is allowed but not required) edit the configuration file for services intended to be used with streamed transfers to set the transfer mode. For TCP and named pipe transports, the transfer mode is propagated as a policy assertion."

Штом ќе се обидете да го тестирате сево ова, следната грешка која би можеле да ја добиете е во врска со maxArrayLength property од readerQuotas елементот во Web.config. Се работи за XML Читачот на XML податоците.

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>

Грешката изгледа вака:

The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 205847.

Во овој случај ќе треба да се зголеми maxArrayLength вредноста затоа што byte[] низата ја преминува големината од 16348. Повторно, максималната вредност која можеме да ја поставиме за maxArrayLength е максимум од Int32 или 2147483647. Штом завршивме со ова, максималниот пренос на податоци помеѓу веб сервисот и конзумерот (клиентната апликација) ќе биде зголемен до таа вредност која ја поставивме претходно.

Во иднина, при пишување на теми од оваа област ќе се фокусирам воглавно со користење на WCF.
(Овој предлог го добив од Vivek, еден од администраторите на CodeASP.NET и познат Microsoft MVP)

Тоа е се.

Се најдобро,
Хајан

Истиов блог пост беше објавен на англиски на:

CodeASP.NET - http://codeasp.net/blogs/hajan/microsoft-net/802/transferring-large-data-when-using-web-services2

ASP.NET - http://weblogs.asp.net/hajan/archive/2010/07/13/transferring-large-data-when-using-web-services.aspx