Zhou's profileSheva's TechSpacePhotosBlogLists Tools Help

Blog


    1/5/2009

    Streaming Media Content Over WCF RESTful Service

    WCF RESTful service API enables you to serve POX type of content over the http transport, and its default content-type header is application/xml if you use DataContractSerializer and application/json if you use DataContractJsonSerializer. If you need to serve up other contents for instance video/audio content, you need to explicitly control the content-type header, this could be achieved again by writing custom message formatter as demonstrated in the previous post, here is the code:

    public class ContentTypeMessageFormatter : IDispatchMessageFormatter
    {
        private IDispatchMessageFormatter formatter;
        private String contentType;
        public ContentTypeMessageFormatter(IDispatchMessageFormatter formatter, String contentType)
        {
            this.formatter = formatter;
            this.contentType = contentType;
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            formatter.DeserializeRequest(message, parameters);
        }

        public Message SerializeReply(MessageVersion messageVersion, Object[] parameters, Object result)
        {
            if (!String.IsNullOrEmpty(contentType))
            {
                WebOperationContext.Current.OutgoingResponse.ContentType = contentType;
            }
            return formatter.SerializeReply(messageVersion, parameters, result);
        }
    }

    public class ContentTypeAttribute : Attribute, IOperationBehavior
    {
        public ContentTypeAttribute(String contentType)
        {
            this.ContentType = contentType;
        }

        public String ContentType
        {
            get;
            set;
        }

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.Formatter = new ContentTypeMessageFormatter(dispatchOperation.Formatter, ContentType);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }

    Then you could directly specify the ContentTypeAttribute at the service operation level, the following service could stream WMV video content over http by turning on the streamed transfer mode of WCF:

    [ServiceContract]
    public interface IMediaService
    {
        [OperationContract]
        [ContentType("audio/x-ms-wmv")]
        [WebGet(UriTemplate = "media/{name}")]
        Stream GetMedia(String name);
    }

    [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
    public class MediaService : IMediaService
    {
        public Stream GetMedia(String name)
        {
            var dir = HttpContext.Current.Server.MapPath("~");
            var file = String.Format("{0}.wmv", name);
            var filePath = Path.Combine(dir, file);
            return File.OpenRead(filePath);
        }
    }

    You turn on streaming in WCF, you need to configure the service as follows:

    <system.serviceModel>
      <
    serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
      <
    services>
        <
    service
         behaviorConfiguration="serviceBehavior"
         name="CustomContentTypeInRESTDemo.MediaService">
          <
    endpoint
           behaviorConfiguration="RestBehaviorConfig"
           binding="webHttpBinding"
           bindingConfiguration="HttpStreaming"
           contract="CustomContentTypeInRESTDemo.IMediaService"/>
        </
    service>
      </
    services>
      <
    bindings>
        <
    webHttpBinding>
          <
    binding name="HttpStreaming" maxReceivedMessageSize="67108864" transferMode="Streamed"/>
        </
    webHttpBinding>
      </
    bindings>
      <
    behaviors>
        <
    endpointBehaviors>
          <
    behavior name="RestBehaviorConfig">
            <
    webHttp/>
          </
    behavior>
        </
    endpointBehaviors>
      </
    behaviors>
    </
    system.serviceModel>

    Streaming audio/video content using RESTful service could be pretty useful, in particular if you need to use WPF’s MediaElement to playback content provided by your service, since MediaElement only allows you to specify a Uri of the media file, the DirectShow has build-in source filter to feed video/audio samples from HTTP transport or local file system, but doesn’t provide a built-in source filter to read samples from arbitrary stream.

    In WPF, you could directly have MediaElement’s Source property pointing to the templated REST Uri:

    <MediaElement Source="http://localhost:8080/MediaService.svc/media/testVideo"/>

    Actually, if you need VCR type of control over the streamed media content, then you need native protocol level support such as RTSP. Windows Media Services is Microsoft’s server implementation of RTSP protocol, and is available in Windows 2003 and Windows 2008. This link illustrates how to configure WMS in Windows 2003.

    1/4/2009

    Include XML Declaration in WCF RESTful Service Response

    When using WCF to write RESTful web services, the default XML response stream doesn’t contain the XML Declaration or XML DOCTYPE. the default XML serialization mechanism used by WCF’s data contract serializer does support emitting XML declaration, but it isn’t turned out by default. You could try using XmlSerializer instead which does support emitting XML declaration,  but if you need stick to data contract serializer, you could try hooking into the WCF serialization process by writing a custom message formatter, the following code contains the full implementation of this approach:

    public class XmlDeclarationMessage : Message
    {
        private Message message;
        public XmlDeclarationMessage(Message message)
        {
            this.message = message;
        }

        public override MessageHeaders Headers
        {
            get { return message.Headers; }
        }

        protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
        {
            // WCF XML serliazation doesn't support emitting XML DOCTYPE, you need to roll up your own here.
            writer.WriteStartDocument();
            message.WriteBodyContents(writer);
        }


        public override MessageProperties Properties
        {
            get { return message.Properties; }
        }

        public override MessageVersion Version
        {
            get { return message.Version; }
        }
    }

    public class XmlDeclarationMessageFormatter : IDispatchMessageFormatter
    {
        private IDispatchMessageFormatter formatter;
        public XmlDeclarationMessageFormatter(IDispatchMessageFormatter formatter)
        {
            this.formatter = formatter;
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            formatter.DeserializeRequest(message, parameters);
        }

        public Message SerializeReply(MessageVersion messageVersion, Object[] parameters, Object result)
        {
            var message = formatter.SerializeReply(messageVersion, parameters, result);
            return new XmlDeclarationMessage(message);
        }
    }

    public class IncludeXmlDeclarationAttribute : Attribute, IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.Formatter = new XmlDeclarationMessageFormatter(dispatchOperation.Formatter);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }

    Then you could specify the IncludeXmlDeclarationAttribute at service operation level, if you need to enable this at service wide level, you could try implementing the IServiceBehavior interface instead.

    [ServiceContract]
    public interface IDemoService
    {
        [OperationContract]
        [IncludeXmlDeclaration]
        [WebGet(UriTemplate = "customers/{name}/")]
        Customer GetCustomer(String name);
    }

    11/10/2007

    WCF Trip - What Happens To BeginInvoke

        Recently I came across Nicholas Allen's blog post talking about how BeginInvoke breaks when used against proxies generated by WCF client runtime, specifically the ChannelFactory. and his conclusion to the misbehaviour exposed by BeginInvoke right here is something like this (quoted from the original article):

    The problem is that BeginInvoke knows about and only works with specific types of proxy objects, which do not include the proxy objects generated by ChannelFactory.

        Actually Nicholas Allen's reasoning here is kinda like a "technical correct but lack of detailed explanation" statement, if you write something like the following, no one can imagine that you are actually doing something wrong:

    String uri = "net.tcp://localhost:2222/Services";
    ChannelFactory<IEchoService> factory = new ChannelFactory<IEchoService>(new NetTcpBinding(), uri);
    IEchoService proxy = factory.CreateChannel();
    EchoDelegate d = new EchoDelegate(proxy.Echo);
    IAsyncResult result = d.BeginInvoke("foo", new AsyncCallback(Callback), null);

       So what really happens here?

       Let's first add some piece of code into the original testing code to check some of presumptions I make on the proxy generated by ChannelFactory:

    Console.WriteLine(System.Runtime.Remoting.RemotingServices.IsTransparentProxy(proxy));
    Console.WriteLine(System.Runtime.Remoting.RemotingServices.GetRealProxy(proxy).GetType());
    Console.WriteLine(result.IsCompleted);

       If running the modified code, you will find some of the interesting bits:

    1. The proxy generated by WCF client runtime aka ChannelFactory is actually a TransparentProxy;
    2. The RealProxy paired with this TransparentProxy is a System.ServiceModel.Channels.ServiceChannelProxy implementation;
    3. When beginInvoking against a proxy generated by ChannelFactory, the call is performed as ordinary synchronous method invocation.

       So what's the happening here? How does WCF relate to the remoting architecture such as TransparentProxy and RealProxy metaphors? and specifically why does BeginInvoke break here?

       To answer those questions, let's first take on the first question, and digg into it, I've spent several hours to examine the implementation of WCF ChannelFactory implementation, one of the greatest discovery I find is that before kicking off the channel stack to process the service request, WCF client runtime will actually intercept every WCF service call by injecting a TransparentProxy and ServiceChannelProxy between WCF service call site and underlying the channel stack. The reason WCF implements the client runtime the way it is is that WCF needs to differ between normal method invocation and WCF service invocation on service proxy objects. how about if you call GetType() on the proxy generated by the ChannelFactory, what type do you expect the GetType() method will return? If you write the code to test, you will get stunned by realizing that GetType() will actually return IEchoService, WTF? How does GetType() method return the interface type object rather than the concrete type object? Actually, WCF has been intercepted the call to GetType(), and revamped it to return the underlying proxied type, thus hiding the real proxy implementation for IEchoService. Another reason WCF intercepts every method call is to differ between synchronous service calls and asynchronous service calls. Imagine you have a WCF service contract like this:

    [ServiceContract]
    public interface IEchoService
    {
        [OperationContract]
        String Echo(String text);
    }

       If you run the svcutil.exe tool to generate client side proxy implementation for async call just as Nicholas Allen suggested in his original article:

       svcutil /language:C# /config:App.config /async net.tcp://localhost:2222/Services

       you will get something like this:

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName="IEchoService")]
    public interface IEchoService
    {
       
        [System.ServiceModel.OperationContract]
        string Echo(string text);
       
        [System.ServiceModel.OperationContract]
        System.IAsyncResult BeginEcho(string text, System.AsyncCallback callback, object asyncState);
       
        string EndEcho(System.IAsyncResult result);
    }

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    public interface IEchoServiceChannel : IEchoService, System.ServiceModel.IClientChannel
    {
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    public partial class EchoServiceClient : System.ServiceModel.ClientBase<IEchoService>, IEchoService
    {
       
        public EchoServiceClient()
        {
        }
       
        public EchoServiceClient(string endpointConfigurationName) : base(endpointConfigurationName)
        {
        }
       
        public EchoServiceClient(string endpointConfigurationName, string remoteAddress) :
                base(endpointConfigurationName, remoteAddress)
        {
        }
       
        public EchoServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress)
        {
        }
       
        public EchoServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress    remoteAddress) : base(binding, remoteAddress)
        {
        }
       
        public string Echo(string text)
        {
            return base.Channel.Echo(text);
        }
       
        public System.IAsyncResult BeginEcho(string text, System.AsyncCallback callback, object asyncState)
        {
            return base.Channel.BeginEcho(text, callback, asyncState);
        }
       
        public string EndEcho(System.IAsyncResult result)
        {
            return base.Channel.EndEcho(result);
        }
    }

        From the above code, we find that WCF follows .NET's asynchronous method invocation pattern quite closely by pairing each service operation call XX with a BeginXX and EndXX async call implementation. the code shown above is really clear and standard, but how does it work out actually? How does a BeginXX call will be performed asynchronously? and how does a EndXX kicks in here to finalize the asynchronous service invocation?

       In order to let the BeginXX and EndXX work as their signature indicate, WCF actually needs to know which service invocation is going to be performed through BeginXX or EndXX calls, to put it another way, WCF needs to know if the BeginXX or EndXX has been called, so it will perform its underlying plumbing to do the magic, in order to get those invocation infomation, WCF needs to have the capability to fine-grained control over the invocation of the service operations exposed by the service contracts. Since TransparentProxy and RealProxy mechanism which is heavily used by the .NET remoting has already haven this capability directly built into the CLR, WCF can leverage this infrastructure to intercept the method calls, and perform its underlying async plumbing at the channel level according to method you are going to invoke.

       Right now, I am almost finishing answering the first question - How does WCF relate to the remoting architecture such as TransparentProxy and RealProxy metaphors? but how about the second question I raised myself, why does BeginInvoke break when all the TP and RP plumbing is in place? This question is much trickier than it seems to be, after a bit of research on the default implementation of TP and RP mechanism used by .NET remoting using both  .NET reflector and the rotor 2.0 implementation of CLR, I finally figure out that for the current implementation of TP and RP mechanism, it only supports asynchronous call when the default RemotingProxy is in place, since the ServiceChannelProxy is WCF's own implementation, it gets ignored by the BeginInvoke mechanism, and the BeginInvoke call against WCF's proxies will be performed synchronously.

       Up until now, all the puzzles has been demystified. BeginInvoke is probably one of the most confusing APIs in the .NET framework as this article and my previous article demonstrates:)