1

I have a WCF web service based on JSON and POST method which have a function called website returning a JSON with more 2KB content. This service has more than 10K requests per second. So, I have lots of textual data to be passed via network and I/O. I think I can reduce this volume of data by compressing the response automatically. I know that by setting a header in client side for accepting zipped content, the client can notify the server that compressed content is acceptable. But how the server can send compressed content?

I have read this link, and implement it. But it works only for SOAP which is xml-based, not JSON. I mean that this configuration:

<customBinding>
  <binding name="BinaryCompressionBinding"> 
    <binaryMessageEncoding compressionFormat="GZip"/> 
    <httpTransport /> 
  </binding>
</customBinding> 

can not work with JSON because we have to use binaryMessageEncoding, where as JSON needs webMessageEncoding and it does not support compressionFormat.

Also, IIS dynamic compression can not help me too. I have added markup it needs to compress JSON.


Update: This is my applicationhost.config:

<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files" minFileSizeForComp="0"> <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" /> <dynamicTypes> <add mimeType="application/json" enabled="true" /> <add mimeType="application/json; charset=utf-8" enabled="true" /> </dynamicTypes> </httpCompression>
Community
  • 1
  • 1
Alireza Mahmoudi
  • 964
  • 8
  • 35
  • What do you mean by added markup and why dynamic compression isn't helping you? – Anestis Kivranoglou Dec 12 '16 at 10:06
  • @AnestisKivranoglou by markup I mean using in webconfig with proper attributes such as doDynamicCompression. Also I have added to applicationhost.config. I think Dynamic compression is used on web pages and the content of a POST request or response is not compressed. – Alireza Mahmoudi Dec 12 '16 at 11:14
  • does your service actually returns "application/javascript" ?? The type has to exactly match . For example for WebApi the added type has to be "application/json; charset=utf-8". If you manage to set dynamic compression on IIS level it actualy works for json responses. – Anestis Kivranoglou Dec 12 '16 at 11:16
  • @AnestisKivranoglou The service return a JSON as a response. Here is the header: Cache-Control: private Content-Length: 1049 Content-Type: application/json; charset=utf-8 Server: Microsoft-IIS/8.5 X-Aspnet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Mon, 12 Dec 2016 11:15:04 GMT Yes I Also added . – Alireza Mahmoudi Dec 12 '16 at 11:21
  • Then you need to add on your applicationhost.config. Warning adding this to your web.config won't work.See-> http://stackoverflow.com/questions/28844960/web-api-gzip-not-being-applied/40910034#40910034 – Anestis Kivranoglou Dec 12 '16 at 11:27
  • I have added this too... – Alireza Mahmoudi Dec 12 '16 at 11:43

1 Answers1

2

As an alternative and more control , you can manually compress your responses with c# code within a Message Inspector.

For WebApi I did something similar using delegating handler. You can wrap my code into a Message Inspector for WCF i think.

Compress WebApi Response using delegating handler

Message Inspectors

Or to use IIS level compression add

<add mimeType="application/json; charset=utf-8" enabled="true" />

On your applicationhost.config. See about json compression enabled mime type here


Update: With special thanks to @AnestisKivranoglou, I have successfully implemented a gzip compression for Json responses of WCF. To make the answer of @AnestisKivranoglou more accurate, I want to add some details to his answer.

You have to change the AfterReceiveRequest and BeforeSendReply of Message Inspector like bellow:

object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            try
            {
                var prop = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                var accept = prop.Headers[HttpRequestHeader.AcceptEncoding];

                if (!string.IsNullOrEmpty(accept) && accept.Contains("gzip"))
                {
                    var item = new DoCompressExtension();
                    OperationContext.Current.Extensions.Add(item);
                }
            }
            catch { }

            return null;
        }


void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
        {
            if (OperationContext.Current.Extensions.OfType<DoCompressExtension>().Count() > 0)
            {
                HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty();
                httpResponseProperty.Headers.Add(HttpResponseHeader.ContentEncoding, "gzip");
                reply.Properties[HttpResponseMessageProperty.Name] = httpResponseProperty;                                
            }            
        }

These 2 snippet of codes let you know whether the client can accept gzip or not! So, you implemented a behaviorExtensions that can capture a request before reply and after receive. Now you need to implement a bindingElementExtensions using this link. Note that you have to fix the GZip problem using this link. Finally it remains a small number of changes listed bellow:

  1. Add an item to ApplyConfiguration function:

case "webMessageEncoding":binding.innerBindingElement = new WebMessageEncodingBindingElement();break;

  1. Change custom binding from innerMessageEncoding="textMessageEncoding" to innerMessageEncoding="webMessageEncoding".

If nothing was missed, you can check and call the service using Post method and Json an receive compressed responses if you add Accept-Encoding:gzip, deflate to your request's header. If you dont add this header you'll receive normal responses which is not compressed.

Community
  • 1
  • 1
Anestis Kivranoglou
  • 7,728
  • 5
  • 44
  • 47
  • Do I have to notify the client that the message is compressed?! And it has to decompressed it? – Alireza Mahmoudi Dec 12 '16 at 11:52
  • The CompressedContent class handles that by adding the "Content-Encoding" header to the response if it is compressed. That is what your client needs to de-compress the response. – Anestis Kivranoglou Dec 12 '16 at 11:57
  • I think, it is not possile to use `GlobalConfiguration.Configuration.MessageHandlers.Insert(0, CompressHandler.GetSingleton());` in WCF Global.asax.cs file. – Alireza Mahmoudi Dec 12 '16 at 14:40
  • yes you will have to implement the equivalent for Message Inspectors . MessageHandlers are WebApi Specific related. Message Inspectors are for WCF – Anestis Kivranoglou Dec 12 '16 at 14:47
  • I implemented the code but I get `500: System.ServiceModel.ServiceActivationException`! I think message inspector has problem with json. – Alireza Mahmoudi Dec 13 '16 at 13:13