First, create the application setup described in my WCF - Establishing a sample app baseline blog entry.
We will then modify the client program so that it encounters some errors while communicating with the WCF Service.
Next, add a new method to ConsoleApp1:
private static string GenerateStringOfCertainLength(int stringLength) { string returnValue = String.Empty; int numTens = stringLength / 10; int remainder = stringLength % 10;
for (int counter = 0; counter < numTens; counter++) { returnValue += "0123456789"; } for (int counter = 0; counter < remainder; counter++) { returnValue += counter.ToString(); }
return returnValue; }
Replace the contents of the try block in ConsoleApp1 with:
ServiceReference1.Service1Client oneService1Client = new ServiceReference1.Service1Client(); ServiceReference1.CompositeType oneCompositeType = new ServiceReference1.CompositeType(); oneCompositeType.StringValue = GenerateStringOfCertainLength(8193); oneCompositeType = oneService1Client.GetDataUsingDataContract(oneCompositeType);
Ctrl-F5 (Debug -> Start Without Debugging)
This may crash Cassini (the VS Web Server) and will result in the following exception:
--oneException=[System.ServiceModel.FaultException: 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 WcfApp1.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 8652.'. Please see InnerException for more details.
Server stack trace: at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at ConsoleApp1.ServiceReference1.IService1.GetDataUsingDataContract(CompositeType composite) at ConsoleApp1.ServiceReference1.Service1Client.GetDataUsingDataContract(CompositeType composite) in C:\dev\Prototyping\WCF\WcfApp1\ConsoleApp1\Service References\ServiceReference1\Reference.cs:line 120 at ConsoleApp1.Program.Main(String[] args) in C:\dev\Prototyping\WCF\WcfApp1\ConsoleApp1\Program.cs:line 17]--
If we send 50000 instead of 8193, we get a different exception:
--oneException=[System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (400) Bad Request. ---> System.Net.WebException: The remote server returned an error: (400) Bad Request. at System.Net.HttpWebRequest.GetResponse() at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout) --- End of inner exception stack trace ---
Server stack trace: at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException) at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout) at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout) at System.ServiceModel.Channels.ClientReliableChannelBinder`1.RequestClientReliableChannelBinder`1.OnRequest(TRequestChannel channel, Message message, TimeSpan timeout, MaskingMode maskingMode) at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout, MaskingMode maskingMode) at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout) at System.ServiceModel.Security.SecuritySessionClientSettings`1.SecurityRequestSessionChannel.Request(Message message, TimeSpan timeout) at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
The out of the box wizard generated service has a couple of limitations in string lengths that it will support.
These are essentially client side errors and can't be usefully debugged on the server side or using tools like Fiddler.
Let's go back to the 8193 character string and solve that issue first. We can do this by modifying the web.config for the WCF web service and the app.config for the console client application.
There is a tool in Visual Studio that gives us a property editor for WCF configuration. You may or may not find it preferable to use the tool over modifying the xml files directly, but that is how I am going to describe the changes we need to make.
If you right click on the web.config in the Solution Explorer and don't see a menu option called "Edit WCF Configuration", do the following to get it added:
In Visual Studio 2008, go to the tools menu, then select "WCF Service Configuration Editor" File -> Exit
Now, right click web.config in Solution Explorer -> Edit WCF Configuration
Select Bindings -> New Binding Configuration... Please select a binding type: wsHttpBinding Select Bindings -> NewBinding0 (wsHttpBinding) Set Configuration -> Name to: CustomWsHttpBindingConfiguration Set ReaderQuotas Properties -> MaxStringContentLength to: 9000
Select Services -> WcfApp1.Service1 -> Endpoints -> (Empty Name) [the first one - not mexHttpBinding] Select Endpoint Properties -> Binding Configuration: CustomWsHttpBindingConfiguration
File -> Save File -> Exit
Any time we update the web.config file, we should update the service reference, even though in this case it is likely unnecessary:
Solution Explorer Right Click ConsoleApp1 -> Service References -> ServiceReference1 Update Service Reference OK
We need to make the same change on the client side as we made on the server side:
Right Click app.config in Solution Explorer -> Edit WCF Configuration Select Bindings -> WSHttpBinding_IService1 (wsHttpBinding) Set ReaderQuotas Properties -> MaxStringContentLength to: 9000 Save & Exit
If we run the application now using 8193, the exception is gone. So, it is now clear how to send strings larger than 8192 characters to a WCF web service (the exception helped point us in the right direction).
Now, let's go back to the 50000 scenario. The above changes are not enough to fix it as we are now exceeding a different character limit.
Right click web.config in Solution Explorer -> Edit WCF Configuration Select Bindings -> CustomWsHttpBindingConfiguration (wsHttpBinding) Set General -> MaxReceivedMessageSize to: 75000 Set ReaderQuotas Properties -> MaxStringContentLength to: 50000 Save & Exit
Right Click app.config in Solution Explorer -> Edit WCF Configuration Select Bindings -> WSHttpBinding_IService1 (wsHttpBinding) Set General -> MaxReceivedMessageSize to: 75000 Set ReaderQuotas Properties -> MaxStringContentLength to: 50000 Save & Exit
Again, just for best practice reasons, we update the service reference:
How did I come up with those numbers? Trial and error. The value for MaxStringContentLength logically matches the length of the string we are trying to send. MaxReceivedMessageSize seems to include extra characters, but it's not clear to me what exactly those extra characters are.
If you increase the values of these two config settings on the server, but not the client, the client starts giving useful error messages (like: The maximum message size quota for incoming messages (70000) has been exceeded.) instead of the mostly useless "(400) Bad Request." which basically means that the server rejected your request and is pretty much unwilling to tell you why.
Please note that increasing the size of these config settings makes your web service more vulnerable to denial of service attacks.
It's pretty easy to exceed the wizard generated values however if you are, for example, trying to send the contents of a text file as a string parameter to a WCF method.
So, now you know how to configure WCF to allow sending larger strings back and forth.
Remember Me
Powered by: newtelligence dasBlog 1.8.5223.2
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.
© Copyright 2010, Michael Maddox
E-mail