Monday, November 2, 2015

String trimming WCF inspector for JSON binding

In my last blog entry I presented a string trimming WCF inspector for SOAP binding. Unfortunately, the approach presented there doesn’t work for Web bound WCF services that use JSON as the communication language.

The difficulty here is that the documentation of possible differences in message inspectors for XML vs JSON is very sparse. Fortunately, the previously mentioned blog entry by Carlos Figueira brings some invaluable details. In particular, Carlos shows that internally a JSON WCF represents the incoming data using XML, unfortunately however, there approach from the SOAP inspector doesn’t work this time. This is because requests are never SOAP envelopes.

There are two possible approaches, either stick with the XML that represents incoming JSON data or work at the JSON level. I took this as a challenge to get the JSON from the XML, modify it and give it back to the message builder. To trim strings I need a completely different approach then, I no longer walk over XML nodes, rather I have a JSON I need to rewrite to another JSON but the actual structure of the data is not known (the inspector works for different WCF methods of different signatures not known to the inspector).

My proposal is as follows, it uses JSON.NET to rewrite JSON with a help of an auxiliary json converter that does actual trimming.

/// <summary>
 /// WCF inspektor trimujący stringi dla webhttpbinding
 /// </summary>
 /// <remarks>
 /// https://code.msdn.microsoft.com/WCF-REST-Message-Inspector-c4b6790b
 /// </remarks>
 public class StringTrimmingWebMessageInspector : IDispatchMessageInspector
 {
     #region IDispatchMessageInspector Members
 
     public object AfterReceiveRequest(
         ref System.ServiceModel.Channels.Message request,
         IClientChannel channel, InstanceContext instanceContext )
     {
         if ( !request.IsEmpty )
         {
             WebContentFormat messageFormat = this.GetMessageContentFormat( request );
             if ( messageFormat == WebContentFormat.Json )
             {
                 MemoryStream ms            = new MemoryStream();
                 XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter( ms );
                
                 request.WriteMessage( writer );
                 writer.Flush();
 
                 ms.Close();
 
                 string messageBody = Encoding.UTF8.GetString(ms.ToArray());
 
                 string convertedBody = JObject.Parse( messageBody )
                    .ToString( Newtonsoft.Json.Formatting.None, new TrimmingConverter() );
 
                 ms = new MemoryStream( Encoding.UTF8.GetBytes( convertedBody ) );
                 XmlDictionaryReader reader = 
                    JsonReaderWriterFactory.CreateJsonReader( ms, XmlDictionaryReaderQuotas.Max ); 
 
                 Message newMessage = Message.CreateMessage( reader, int.MaxValue, request.Version );
                 newMessage.Properties.CopyProperties( request.Properties );
                 request = newMessage; 
             }               
         }
 
         return null;
     }
 
     private WebContentFormat GetMessageContentFormat( Message message )
     {
         WebContentFormat format = WebContentFormat.Default;
         if ( message.Properties.ContainsKey( WebBodyFormatMessageProperty.Name ) )
         {
             WebBodyFormatMessageProperty bodyFormat;
             bodyFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
             format = bodyFormat.Format;
         }
 
         return format;
     }
 
     public void BeforeSendReply( ref System.ServiceModel.Channels.Message reply, object correlationState )
     {
     }
 
     #endregion
 }
 
 public class TrimmingConverter : JsonConverter
 {
     public override bool CanConvert( Type objectType )
     {
         return objectType == typeof( string );
     }
     public override bool CanRead { get { return true; } }
     public override object ReadJson( 
        JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
     {
         if ( reader.Value != null && reader.Value is string )
             return ( (string)reader.Value ).Trim();
         else
             return reader.Value;
     }
     public override bool CanWrite { get { return true; } }
     public override void WriteJson( 
        JsonWriter writer, object value, JsonSerializer serializer )
     {
         if ( value is string )
             serializer.Serialize( writer, ( (string)value ).Trim() );
         else
             serializer.Serialize( writer, value );
     }
 }

No comments: