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
- }
- }
|