| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 | using System;using System.ServiceModel;using System.ServiceModel.Channels;using System.ServiceModel.Description;using System.ServiceModel.Dispatcher;using System.Text;using System.Xml;using System.Runtime.Serialization;namespace iBemsDataService{    class JSONPSupportInspector : IDispatchMessageInspector    {        // Assume utf-8, note that Data Services supports        // charset negotation, so this needs to be more        // sophisticated (and per-request) if clients will         // use multiple charsets        private static readonly Encoding encoding = Encoding.UTF8;        #region IDispatchMessageInspector Members        public object AfterReceiveRequest( ref Message request , IClientChannel channel , InstanceContext instanceContext )        {            if( request.Properties.ContainsKey( "UriTemplateMatchResults" ) )            {                HttpRequestMessageProperty httpmsg = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];                UriTemplateMatch match = (UriTemplateMatch)request.Properties["UriTemplateMatchResults"];                string format = match.QueryParameters["$format"];                if( "json".Equals( format , StringComparison.InvariantCultureIgnoreCase ) )                {                    // strip out $format from the query options to avoid an error                    // due to use of a reserved option (starts with "$")                    match.QueryParameters.Remove( "$format" );                    // replace the Accept header so that the Data Services runtime                     // assumes the client asked for a JSON representation                    httpmsg.Headers["Accept"] = "application/json;odata=verbose, text/plain;q=0.5";                    httpmsg.Headers["Accept-Charset"] = "utf-8";                    string callback = match.QueryParameters["$callback"];                    if( !string.IsNullOrEmpty( callback ) )                    {                        match.QueryParameters.Remove( "$callback" );                        return callback;                    }                }            }            return null;        }        public void BeforeSendReply( ref Message reply , object correlationState )        {            if( correlationState != null && correlationState is string )            {                // if we have a JSONP callback then buffer the response, wrap it with the                // callback call and then re-create the response message                string callback = (string)correlationState;                bool bodyIsText = false;                HttpResponseMessageProperty response = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;                if( response != null )                {                    string contentType = response.Headers["Content-Type"];                    if( contentType != null )                    {                        // Check the response type and change it to text/javascript if we know how.                        if( contentType.StartsWith( "text/plain" , StringComparison.InvariantCultureIgnoreCase ) )                        {                            bodyIsText = true;                            response.Headers["Content-Type"] = "text/javascript;charset=utf-8";                        }                        else if( contentType.StartsWith( "application/json" , StringComparison.InvariantCultureIgnoreCase ) )                        {                            response.Headers["Content-Type"] = contentType.Replace( "application/json" , "text/javascript" );                        }                    }                }                XmlDictionaryReader reader = reply.GetReaderAtBodyContents();                reader.ReadStartElement();                string content = JSONPSupportInspector.encoding.GetString( reader.ReadContentAsBase64() );                if( bodyIsText )                {                    // Escape the body as a string literal.                    content = "\"" + QuoteJScriptString( content ) + "\"";                }                content = callback + "(" + content + ")";                Message newreply = Message.CreateMessage( MessageVersion.None , "" , new Writer( content ) );                newreply.Properties.CopyProperties( reply.Properties );                reply = newreply;            }        }        private static string QuoteJScriptString( string s )        {            if( string.IsNullOrEmpty( s ) )            {                return string.Empty;            }            StringBuilder builder = null;            int startIndex = 0;            int count = 0;            for( int i = 0 ; i < s.Length ; i++ )            {                char ch = s[i];                if( ((((ch == '\r') || (ch == '\t')) || ((ch == '"') || (ch == '\\'))) || (((ch == '\n') || (ch < ' ')) || ((ch > '\x007f') || (ch == '\b')))) || (ch == '\f') )                {                    if( builder == null )                    {                        builder = new StringBuilder( s.Length + 6 );                    }                    if( count > 0 )                    {                        builder.Append( s , startIndex , count );                    }                    startIndex = i + 1;                    count = 0;                }                switch( ch )                {                    case '\b':                        builder.Append( @"\b" );                        break;                    case '\t':                        builder.Append( @"\t" );                        break;                    case '\n':                        builder.Append( @"\n" );                        break;                    case '\f':                        builder.Append( @"\f" );                        break;                    case '\r':                        builder.Append( @"\r" );                        break;                    case '"':                        builder.Append( "\\\"" );                        break;                    case '\\':                        builder.Append( @"\\" );                        break;                    default:                        if( (ch < ' ') || (ch > '\x007f') )                        {                            builder.AppendFormat( System.Globalization.CultureInfo.InvariantCulture , @"\u{0:x4}" , (int)ch );                        }                        else                        {                            count++;                        }                        break;                }            }            string result;            if( builder == null )            {                result = s;            }            else            {                if( count > 0 )                {                    builder.Append( s , startIndex , count );                }                result = builder.ToString();            }            return result;        }        #endregion        class Writer : BodyWriter        {            private string content;            public Writer( string content )                : base( false )            {                this.content = content;            }            protected override void OnWriteBodyContents( XmlDictionaryWriter writer )            {                writer.WriteStartElement( "Binary" );                byte[] buffer = JSONPSupportInspector.encoding.GetBytes( this.content );                writer.WriteBase64( buffer , 0 , buffer.Length );                writer.WriteEndElement();            }        }    }    // Simply apply this attribute to a BemsDataService-derived class to get    // JSONP support in that service    [AttributeUsage( AttributeTargets.Class )]    public class JSONPSupportBehaviorAttribute : Attribute , IServiceBehavior    {        #region IServiceBehavior Members        void IServiceBehavior.AddBindingParameters( ServiceDescription serviceDescription , ServiceHostBase serviceHostBase , System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints , BindingParameterCollection bindingParameters )        {        }        void IServiceBehavior.ApplyDispatchBehavior( ServiceDescription serviceDescription , ServiceHostBase serviceHostBase )        {            foreach( ChannelDispatcher cd in serviceHostBase.ChannelDispatchers )            {                foreach( EndpointDispatcher ed in cd.Endpoints )                {                    ed.DispatchRuntime.MessageInspectors.Add( new JSONPSupportInspector() );                }            }        }        void IServiceBehavior.Validate( ServiceDescription serviceDescription , ServiceHostBase serviceHostBase )        {        }        #endregion    }}
 |