MessageSize does matter

WCF is ingericht in het versturen en ontvangen van berichten via
verschillende soorten bindingen. De vraag- en antwoordberichten kunnen hierbij
zelfs vele megabytes groot zijn. De maximale grootte wordt voornamelijk bepaald door het type binding, zo heeft MSMQ een limiet van vier megabytes. Maar standaard is de grootte veel beperkter ingesteld.

Size DOES matter
Deze week liepen we tegen een dergelijke grens aan, maar:

1. Het was de grootte van het aanvraagbericht welke problemen gaf, niet het
antwoord

2. We gebruiken wsHTTP

We wilden namelijk een grote lijst met ruwe data laten verwerken. Dit
gebeurt stateless, de aanvraag opdelen in kleine stukken is geen optie. We
kregen namelijk al problemen bij het versturen vanaf iets meer dan acht KB.

System.ServiceModel.FaultException`1 was unhandled
Message=”The formatter threw an exception while trying to deserialize
the message: There was an error while trying to deserialize parameter
http://tempuri.org/:composite. The InnerException message was ‘There was an
error deserializing the object of type CompositeType. The maximum string content
length quota (8192) has been exceeded while reading XML data. This quota may be
increased by changing the MaxStringContentLength property on the
XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1,
position 9020.’.  Please see InnerException for more details.”

Bij meer dan negentig KB kregen we de volgende melding:

System.ServiceModel.ProtocolException was unhandled
Message=”The remote server returned an unexpected response: (400) Bad
Request.”

Beide meldingen komen voort uit de standaard instellingen van de binding.
Maar wat zijn die instellingen dan? Waar kan je die vinden?

Ik heb hiervoor een testsituatie gebouwd: een WCF webservice host wordt
aangeroepen door een console applicatie. De host ondersteunt  één methode welke een string accepteert en een string retourneert. De methode geeft een string terug met de inhoud van de  tien keer de parameter achter elkaar. Als je de
cliëntreferentie laat genereren, kijk dan eens in de app.config van de cliënt.
Daar is een complete bindingconfiguratie aan toegevoegd:


<bindings>

  <wsHttpBinding>

    <binding name="WSHttpBinding_IService" ...

    </binding>

  </wsHttpBinding>

</bindings>

Deze bindingconfiguratie wordt aan het ABC (adres, binding en contract)
toegevoegd via een extra attribuut:


<endpoint address="http://localhost:1234/WCFService/Service.svc"
          binding="wsHttpBinding"
          bindingConfiguration="WSHttpBinding_IService"
          contract="ServiceReference.IService"
          name="WSHttpBinding_IService">

Kijk nu eens naar de host web.config. Deze is veel eenvoudiger:


  <endpoint address="" binding="wsHttpBinding" contract="IService" >

Op de host is de binding blijkbaar al standaard geconfigureerd en deze
settings zijn in de cliënt netjes herhaald en uitgeschreven. Let wel: deze
instellingen moeten wel aan beide kanten gelijk aan elkaar gemaakt worden.

Tussen de binding-instellingen staat ook de al eerder genoemde instelling
maxStringContentLength. Als je deze in de app.config aanpast
zal er niet veel gebeuren… Zoals hiervoor al gezegd, cliënt en host moeten op
elkaar aansluiten. Het is dus zaak de aangepaste binding configuratie ook de
host door te voeren. Kopieer dus de <bindings>
instellingen uit de app.config gewoon door naar de web.config. Vergeet
niet in het EndPoint element het attribuut
bindingConfiguration=”WSHttpBinding_IService” op te nemen.

Dit helpt in eerste instantie wel. Maar met wat grotere berichten krijgen we
al snel de melding:

System.ServiceModel.CommunicationException was unhandled
  Message=”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.”

Door dus ook de MaxReceivedMessageSize aan beide zijden te
vergroten wordt het mogelijk om grote berichten te versturen, en ontvangen. Bij
veel voorbeelden op het internet worden gewoon alle instellingen verminkt, ik
heb hier willen aantonen dat met goed lezen en enkele eenvoudige aanpassingen de problemen al opgelost kunnen worden.

Het is hierbij aardig om te weten dat ‘recieve’ altijd vanuit de context van
de cliënt of de host geldt. De MaxReceivedMessageSize in de
app.config geldt bij de cliënt dus voor de berichten vanaf de host richting de
cliënt, nav. een aanvraag.

Kijk nog eens naar de meldingen bovenaan in deze blog.  De eerste
FaultException werd gegooid omdat de cliënt een te lang antwoord moest ontvangen (tien maal de aangeboden tekst van acht KB). De http Error 400 Bad
Request was vrijwel zeker dezelfde fout aan de host kant door de verminkte
request (te lange parameter). Helaas wordt dit als een Bad Request teruggegooid, de host neemt het bericht gewoon niet in ontvangst zodra er teveel data ontvangen wordt.

Kijk voor meer informatie over de verschillende binding configuraties hier zoals die van de wsHttp.