We needed to add GZip compression support to a .NET 3.5sp1 client application using WCF for REST POX web services. The responses were sent compressed using GZIP, so the client needed to be able to decompress these, before passing up the stack to the Data Serialisers and application code layers.
The server in this instance is a Java EE web application returning XML, GZipped using a filter servlet. Implementing the server side filter is fairly trivial, however it's companion in .NET on the client side is not.
Since this gave me a little bit of a challenge recently (very little documentation around or good examples) I thought I'd put up a quick blog about it. It's not that there's a lot of code involved, it's understanding the WCF with sufficient clarity to be able to extend it, and debug it when it's not quite right.
One of the issues that crops up straight away is that most examples of extending the WCF for compression are based around the WS.* web service support, the original incarnation of WCF.
However, later versions of the WCF added support for REST/POX web services, and this is where I wanted to add the GZIP compression support. You can not mix WS.* and Rest (aka Web in WCF) web services in the WCF, without creating a runtime error as the WCF channel is instantiated.
So, enough of the preamble already, lets cut to the chase, what's needed?
Well, assuming you have a WebHttpBinding to start with...
WebHttpBinding webHttpBinding = new WebHttpBinding();
Then, in order to add the GZIP compression support, we need to pull apart the binding, into its constituent elements and then reassemble it, adding in the new binding element.
I enlisted the help of a content mapper to force the content to XML:
public class XMLMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Xml; // always
}
}
First, get the binding elements out of the WebHttpBinding, using the CreateBindingElements() function.
BindingElementCollection bec = webHttpBinding.CreateBindingElements();
WebMessageEncodingBindingElement wmbe = bec.Remove();
HttpTransportBindingElement htbe = bec.Remove();
wmbe.ContentTypeMapper = new XMLMapper();
GZipMessageEncodingBindingElement compBindingElement = new GZipMessageEncodingBindingElement(wmbe);
bec.Add(compBindingElement);
bec.Add(htbe);
CustomBinding cb = new CustomBinding(bec);
binding = cb;
HttpRequestMessageProperty httpRequestMessageProperty = new HttpRequestMessageProperty();
httpRequestMessageProperty.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip");
httpRequestMessageProperty.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip");
Ok, so now binding is a rebuilt binding, based on WebHttpBinding where a GZIP encoder has been used to wrap the WebMessageEncodingBindingElement from the original binding's binding elements.
Note that the HttpTransportBindingElement must be the last binding element in the binding element collection (protocol stack) or a runtime error will be thrown when trying to instantiate the channel.
Now we can create the channel in the normal way:
// create a channel factory
ChannelFactorycf = new ChannelFactory (binding, hostPath);
// create webhttpbehavior for rest / pox get/invoke behaviour support
WebHttpBehavior webHttpBehavior = new WebHttpBehavior();
webHttpBehavior.DefaultOutgoingRequestFormat = WebMessageFormat.Xml;
webHttpBehavior.DefaultOutgoingResponseFormat = WebMessageFormat.Xml;
// add webhttpbehahvior to channelfactory endpoint
cf.Endpoint.Behaviors.Add(webHttpBehavior);
if (ENABLE_COMPRESSION)
{
// add our custom behavior...
cf.Endpoint.Behaviors.Add(new HttpEndpointBehavior());
}
// create our IService channel
channel = cf.CreateChannel();
So far so good, but what about this GZipMessageEncodingBindingElement?
Well, this can be found in the Microsoft WCF samples:
http://msdn.microsoft.com/en-us/library/ms751458.aspx
Detailed steps of integrating the MS WCF GZip example into your code:
http://www.vistax64.com/indigo/113763-wcf-client-j2ee-server-using-gzip.html
Note: I had to make some minor modifications to make my client work correctly, details can be provided if anyone leaves a comment expressing an interest.
Further Resources:
Various threads discussing similar problems:
http://social.msdn.microsoft.
http://social.msdn.microsoft.
http://aspnet.codeplex.com/
Neomax - provide software products to extend the WCF framework, including support for compression:
http://www.noemax.com/
Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA
.
4 comments:
Hi, Louis
Your article seems is what I'm looking for. Can I use this approach with ADO.NET data services (Astoria)?
Thanks,
Dima
I can't get this to work with Json instead of XML...
Hi Louis,
Is there any way to send the request gzipped? i have a large content to post which i need to send as gzip
Post a Comment