Welcome to Windows Communication Foundation (WCF)
Top Tasks :

WCF Team Bloggers

Browse by Tags

All Tags » HTTP » Indigo   (RSS)

  • Streaming Web Content

    How do I deliver content from a WCF service as part of a web page? Web page content in this case typically refers to HTML, images, or other data that is directly consumed by the web browser rather than an application running in the web browser. There are a few things you need to do to make your web service serve up content in a way that's indistinguishable from an ordinary web server. I'll serve up a static image at a fixed location for this example but you can get as fancy as you'd like. The first thing you need is the right contract. The initial page load is ordinarily retrieved using the HTTP GET verb rather than the HTTP POST verb assumed by web services. I'll set that up as part of my contract using the WebGet attribute to set the verb and a URI template to set the address. [ServiceContract] public interface IService { [OperationContract] [WebGet(UriTemplate = "/image" )] Stream GetImage(); } The second thing you need is the right content type. Although web browsers can try to autodetect content, you should specify the content type if it is known. This allows the web browser to process the content correctly inline. public class Service : IService { public Stream GetImage() { WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg" ; return new FileStream( "c:\\test.jpg" , FileMode.Open, FileAccess.Read); } } Finally, you may notice that while I've done everything needed in the service implementation to enable streaming, content can only be streamed if the binding supports this as well. When using WebServiceHost, the default bindings do not support streamed content. This may be hard to spot because the typical files are small and a test program running on the same machine completes the transfers before streaming would make a difference. I've wrapped the service implementation in this example to intentionally slow down the transfer to make the difference more apparent. The following code demonstrates enabling streaming on the binding. You can change the transfer mode back to Buffered to observe the difference. Streaming requires support in the receiving application as well to make a difference. Using a large, progressive encoded image will demonstrate this. using System; using System.IO; using System.ServiceModel; using System.ServiceModel.Web; using System.Threading; public class SlowStream : Stream { Stream innerStream; public SlowStream(Stream innerStream) { this .innerStream = innerStream; } public override bool CanRead { get { return Read More...
  • Avoiding Address Filters

    The address filter mode that we looked at last time solved the problem of funneling all of the messages with a given prefix address to our service instance. Changing the filter mode still left us with the problem of dispatching from that universal contract to all of the logical operations that live inside the address space. This is exactly the problem that UriTemplate solves. By combining templates and WebServiceHost, both of the problems get taken care of for us. Here is an equivalent contract and service implementation with some more semantics filled in for a particular application. All I've done is pick out part of the address space that I want to assign some implementation to. [ServiceContract] public interface IService2 { [OperationContract] [WebGet(UriTemplate = "/resource/{index}" )] string Get( string index); [OperationContract] [WebInvoke(UriTemplate = "/resource" )] void Add( string value ); } public class Service2 : IService2 { public string Get( string index) { Console.WriteLine( "Get {0} {1}" , WebOperationContext.Current.IncomingRequest.Method, OperationContext.Current.IncomingMessageHeaders.To); return null ; } public void Add( string value ) { Console.WriteLine( "Add {0} {1}" , WebOperationContext.Current.IncomingRequest.Method, OperationContext.Current.IncomingMessageHeaders.To); } } Hosting this service is basically the same. I can take out the endpoint definition because that gets inferred automatically. WebServiceHost host = new WebServiceHost( typeof (Service2), new Uri( "http://localhost:8000/" )); host.Open(); Client(); Console.ReadLine(); host.Close(); Finally, I can use the exact same client code as last time even though in the service I've changed my way of writing the service from a centralized approach to an address-based approach. ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>( new WebHttpBinding()); factory.Open(); IRequestChannel proxy = factory.CreateChannel( new EndpointAddress( "http://localhost:8000/" )); using ( new OperationContextScope((IContextChannel)proxy)) { Message request = Message.CreateMessage(MessageVersion.None, string .Empty, "data" ); request.Headers.To = new Uri( "http://localhost:8000/resource" ); WebOperationContext.Current.OutgoingRequest.Method = "POST" ; proxy.Request(request); } using ( new OperationContextScope((IContextChannel)proxy)) { Message request = Message.CreateMessage(MessageVersion.None, string .Empty); request.Headers.To = new Uri( "http://localhost:8000/resource/1" Read More...
  • Web Address Filters

    Here is a basic service that defines a universal contract to program against for building a simple HTTP application. The service doesn't do anything, but you can ignore that in this example. [ServiceContract] public interface IService { [OperationContract(Action = "*" , ReplyAction = "*" )] Message Request(Message msg); } public class Service : IService { public Message Request(Message msg) { Console.WriteLine( "{0} {1}" , WebOperationContext.Current.IncomingRequest.Method, msg.Headers.To); return null ; } } You might expect to run this service in a straightforward hosting environment. ServiceHost host = new ServiceHost( typeof (Service), new Uri( "http://localhost:8000/" )); host.AddServiceEndpoint( typeof (IService), new WebHttpBinding(), "" ); host.Open(); Console.ReadLine(); host.Close(); And, talk to it with the ordinarily convoluted client programming model. ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>( new WebHttpBinding()); factory.Open(); IRequestChannel proxy = factory.CreateChannel( new EndpointAddress( "http://localhost:8000/" )); using ( new OperationContextScope((IContextChannel)proxy)) { Message request = Message.CreateMessage(MessageVersion.None, string .Empty, "data" ); request.Headers.To = new Uri( "http://localhost:8000/resource" ); WebOperationContext.Current.OutgoingRequest.Method = "POST" ; proxy.Request(request); } using ( new OperationContextScope((IContextChannel)proxy)) { Message request = Message.CreateMessage(MessageVersion.None, string .Empty); request.Headers.To = new Uri( "http://localhost:8000/resource/1" ); WebOperationContext.Current.OutgoingRequest.Method = "GET" ; WebOperationContext.Current.OutgoingRequest.SuppressEntityBody = true ; proxy.Request(request); } However, when you put these pieces together, it fails to work. The client turns out to be fine but there's a problem in the way that the service is hosted. More accurately, there's a problem with the interaction between the way the service is hosted and the way it's defined. By specifying an address for hosting the application, we're claiming all of the messages that go to that address. We're also claiming all of the messages that go to suffixes of that address as long as someone else hasn't claimed them first. Unfortunately, the endpoint doesn't know what to do with these extra messages. It just ignores them. What we want is for the endpoint to just take everything and let the service implementation decide what the addresses Read More...
  • Configuring SSL Host Headers

    Host headers in IIS are a way to associate multiple names with a single address. The typical use of host headers is to be able to host more than one web site at a single IP address by giving each of the web sites a distinct DNS name. Host headers also play a role in WCF beyond the definition of a web site. Metadata for a web service, such as that appearing WSDL, uses host headers as a way to pick a preferred name when talking about the service. The user interface for setting host headers is relatively straightforward when the web site is hosted over HTTP but becomes a challenge when the web site is hosted over HTTPS. Here are the command line equivalents that you can use to set HTTPS host headers. On IIS 6, you need to know the id of the web site. Assuming that SSL is taking place on the default port, the command looks like this. cscript.exe adsutil.vbs set w3svc/<id>/SecureBindings ":443:<header>" On IIS 7, the command line looks very different due to the more flexible but complicated support for different web site bindings. You can also use a name that's meaningful for you to distinguish web sites. appcmd set site /site.name:<name> /+bindings.[protocol='https',bindingInformation='*:443:<header>'] To keep the example simple, I'm assuming that you're adding a new binding rather than modifying an existing binding. Next time: Transaction Header Magic Read More...
  • Adding Headers to a Call (HTTP Version)

    Yesterday I talked about adding SOAP headers to an outgoing request using a variety of different methods. The most straightforward method was to create an OperationContextScope in your application code to establish an OutgoingMessageHeaders collection. Although HTTP headers are similar in spirit to SOAP headers, manipulating an HTTP header through code looks a bit different. SOAP headers are elevated to a special significance in the programming model. Everything else, including HTTP headers, is relegated to a general-purpose but distinctly second-class collection of message properties. On the OperationContextScope you'll find a parallel OutgoingMessageProperties collection that can be used for HTTP headers. In Orcas, the plain OperationContext also works as a WebOperationContext that gives the same first-class programming model to HTTP headers as you get with SOAP headers. Here's a comparison of the two approaches. using ( new OperationContextScope((IClientChannel)proxy)) { HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); requestProperty.Headers[ "X-header" ] = "value1" ; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty; proxy.Operation(); } using ( new OperationContextScope((IClientChannel)proxy)) { WebOperationContext.Current.OutgoingRequest.Headers[ "X-header" ] = "value2" ; proxy.Operation(); } Next time: Configuring SSL Host Headers Read More...
  • How WebServiceHost Works

    WebServiceHost is a new feature in Orcas that makes it easy to put up simple web services that are built on HTTP and POX. However, there's no requirement that forces you to build REST and POX services using WebServiceHost. WebServiceHost exists to make a simple case easy, but you're not locked into using that approach if the simple case doesn't apply to you. Here's everything behind WebServiceHost if you want to build your own. WebServiceHostFactory exists to bootstrap WebServiceHost when building a web site in IIS WebServiceHost disables any service metadata or help pages so that they don't steal any part of the URI space under your web site WebServiceHost generates endpoints for all of your contract types with a WebHttpBinding so that you don't have to describe the service endpoints in a configuration file WebServiceHost adds a WebHttpBehavior to all of your service endpoints so that Get and Invoke operations in your service contract work without any additional setup Next time: Security Session Inactivity Read More...
  • Messaging Additions in Orcas, Part 4

    Today wraps up the series on detailed messaging changes in Orcas. You can get the whole series here as well as the previous high-level overview of new Orcas features I did. Messaging Additions in Orcas Messaging Additions in Orcas, Part 2 Messaging Additions in Orcas, Part 3 Now, let's go on with the list. I've got one last feature to cover and then some of the more notable bug fixes that were reported by customers. If you need one of the bug fixes, you can get them by either installing Orcas or the .NET framework 3.0 service pack. Enhancements for web programming . RSS and ATOM syndication, partial trust, JSON, AJAX, and HTTP application programming are all covered reasonably well in the high-level overview so I didn't break them out this time. We no longer make shutdown slow. It took a somewhat rare machine configuration but the various services we run for port sharing and activation could prevent the machine from shutting down until they timed out. Copying a POX message. There aren't any standard channels that buffer messages and are used with HTTP under MessageVersion.None. However, if you write a message inspector, then you need to copy the message before reading it and that now works. Starting a listener while hosted in IIS. I don't recommend starting an independent web service from inside of a web service hosted in IIS. We've made the threading work in this service-within-service case but you're still at the mercy of IIS deciding when to deactivate the outermost service. Emptier messages. When doing POX we have to surface messages even when the HTTP payload is empty so that you have an object to get your HTTP message properties from. Until now though, when we did that conversion those messages would stop reporting that they were empty. Next time: Private Data Members Read More...
  • Messaging Additions in Orcas, Part 3

    Now that I've covered several of the new feature additions in Orcas I also want to include mention of some of the fixes done to improve interoperability with other platforms. Allowing an empty SOAPAction. Previously we required that the HTTP SOAPAction header exactly match the addressing action. We now let you process messages with an empty SOAPAction as many other systems were processing messages based only on the addressing action. Handling empty messages that claim to be chunked. Previously we weren't able to process messages that said that they were chunked but didn't have any content. Some systems were transmitting every HTTP message as chunked (even the empty ones) so we now handle this case gracefully. Flexible content types. Previously we required that the character set parameter for an HTTP content type be the first parameter in the list. Some systems were generating content types with additional parameters in various orders so we've removed any dependency on the parameter order. Parsing MTOM includes. Previously we only supported the canonicalized format for the Include element of an MTOM message. Another system was generating MTOM messages where the elements were not canonicalized so we've added support to read those messages. Next time: Messaging Additions in Orcas, Part 4 Read More...
  • Manual Context Management

    How do I manually manage the context when sharing a client object? The default mode when using a context binding is for the context to be managed internally by the context channel underneath the client proxy. This is similar to how by default cookies are managed by an HTTP channel to send and receive cookie context. With an HTTP channel you can disable automatic cookie management and control the context yourself. There is a similar process that you can use to take control for a context binding. Here's a comparison of the two processes. You can get the code for HTTP by using the link above and with the further details on custom cookie handling so I won't print it again. With HTTP, you first need to turn off automatic cookie handling by setting the AllowCookies property on the HTTP transport binding element to false. With a context binding, you first need to turn off automatic context handling by setting the Enabled property on the context manager to false. IContextManager contextManager = channel.GetProperty<IContextManager>(); contextManager.Enabled = false ; Then, for HTTP you attach an HttpRequestMessageProperty that contains the desired cookies to a message using an OperationContextScope. With a context binding, you use the same OperationContextScope approach but attach the appropriate ContextMessageProperty instead. using ( new OperationContextScope(client.InnerChannel)) { ContextMessageProperty contextProperty = new ContextMessageProperty(contextData); OperationContext.Current.OutgoingMessageProperties[ContextMessageProperty.Name] = contextProperty; client.DoOperation(); } Next time: Messaging Additions in Orcas Read More...
  • Why Dual is Reliable

    You may have noticed that bindings use two different classes for configuring reliability: ReliableSession and OptionalReliableSession. The only difference between the two is that OptionalReliableSession has an Enabled property that allows the reliable session to be turned off. If you only have a ReliableSession to work with, then there is no way for it to be disabled. The only standard binding that uses ReliableSession is WSDualHttpBinding. All of the other standard bindings that support reliability, such as NetTcpBinding and WSHttpBinding, use OptionalReliableSession. Why is WSDualHttpBinding the only one that has to be reliable? The reason for this is that WSDualHttpBinding has to coordinate together two different connections. The stack of binding elements for WSDualHttpBinding looks like this: TransactionFlowBindingElement ReliableSessionBindingElement SecurityBindingElement (optional) CompositeDuplexBindingElement OneWayBindingElement TextMessageEncodingBindingElement (or MtomMessageEncodingBindingElement) HttpTransportBindingElement Composite duplex splits the binding into separate input and output connections, and relies on a higher-level component to correlate the two. There are many ways to provide that correlation, one of which is a reliable session. Requiring that the reliable session be present matches the most common usage pattern and allows the other potential correlation mechanisms to be omitted without having to validate the resulting binding configuration. That's why WSDualHttpBinding requires a reliable session. If you have a different correlation mechanism that you want to use together with composite duplex, then you can build that with a custom binding. Next time: Context Channel Shapes Read More...
  • Runtime Limits in IIS

    Does the IIS HTTP runtime configuration affect a WCF application? Yes, when the application is using IIS to host its HTTP endpoints. A WCF application that lives in IIS is an IIS application as well, even if you aren't explicitly using ASP.NET compatibility mode. Now, most of the settings for IIS don't make as much sense for WCF as for general web applications, but if you look at the httpRuntime configuration element, then you'll see some that apply. < configuration > < system.web > < httpRuntime executionTimeout ="110" maxRequestLength ="4096" requestLengthDiskThreshold ="80" useFullyQualifiedRedirectUrl ="false" minFreeThreads ="8" minLocalRequestFreeThreads ="4" appRequestQueueLimit ="5000" enableKernelOutputCache ="true" enableVersionHeader ="true" requireRootedSaveAsPath ="true" enable ="true" shutdownTimeout ="90" delayNotificationTimeout ="5" waitChangeNotification ="0" maxWaitChangeNotification ="0" requestPriority ="Normal" enableHeaderChecking ="true" sendCacheControlHeader ="true" apartmentThreading ="false" /> </ system.web > </ configuration > The setting that is most common to impact a WCF application is the runtime limit on maxRequestLength. This setting is intended to limit people making large file uploads but the usage patterns of web services seem to make it more likely for people to build applications with large request messages than in traditional HTTP applications. Note that the setting is specified in kilobytes so it is by default much larger than the maximum received message size in WCF. However, if you're dealing with messages of more than a few megabytes, you may run into this limit and be puzzled why your messages are failing even though the application quotas are sufficient. Next time: Sending to MSMQ with Integrated Authentication Read More...
  • Building on Custom Cookie Handling

    By request I expanded the code in the earlier article on custom cookie handling to show a more interesting sample. This time I illustrated making the initial request and capturing a cookie rather than echoing a cookie that the client already had. This sample is specifically written to not use proxies or any of the web programming features so that you can see exactly what goes on under the covers. I threw in a demonstration of CookieContainer for integration but you can use any mechanism you'd like to manage the cookies. You can run this code against your favorite site that distributes cookies. I picked a random site that advertised having a cookie tester. There's no guarantee that this site will be there tomorrow. using System; using System.Net; using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; class CookieTest { static void Main( string [] args) { string address = "http://www.tempesttech.com/cookies/cookietest1.asp" ; Binding binding = new CustomBinding( new TextMessageEncodingBindingElement(MessageVersion.None, Encoding.UTF8), new HttpTransportBindingElement() ); IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(); IRequestChannel channel = null ; bool success = false ; try { factory.Open(); channel = factory.CreateChannel( new EndpointAddress(address)); channel.Open(); Message request = Message.CreateMessage(MessageVersion.None, null ); HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); requestProperty.Method = "GET" ; requestProperty.SuppressEntityBody = true ; request.Properties[HttpRequestMessageProperty.Name] = requestProperty; Message response = channel.Request(request); request.Close(); HttpResponseMessageProperty responseProperty = response.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; CookieContainer container = new CookieContainer(); container.SetCookies( new Uri(address), responseProperty.Headers[HttpResponseHeader.SetCookie]); response.Close(); foreach (Cookie cookie in container.GetCookies( new Uri(address))) { Console.WriteLine(cookie); } channel.Close(); factory.Close(); success = true ; } catch (TimeoutException e) { Console.WriteLine(e); } catch (CommunicationException e) { Console.WriteLine(e); } finally { if (!success) { if (channel != null ) { channel.Abort(); } factory.Abort(); } } Console.ReadLine(); } } Next time: Producing Typed Messages Read More...
  • Programming for the Web in Orcas

    Steve Maine has put together an index of documentation for all of the new WCF web programming features in Orcas. This includes the support for: JSON RSS and Atom syndication feeds URI templates HTTP enhancements As well as features that web developers tend to care about more than average, such as partial trust hosting. Read More...
  • Custom Cookie Handling

    Cookies are the de facto correlation protocol for web applications, which means HTTP applications rather than SOAP. Most uses of cookies in web services are quite simplistic with the standard cookie container behavior sufficiently up to the task of handling the exchange. Today's article covers the times when the automatic cookie handling behavior is insufficient. To get started with custom cookie handling you first need to turn off cookies. This may sound terribly confusing but that's because the AllowCookies property is so badly named. This property controls whether a CookieContainer is setup and used to automatically copy cookies around and set the value of the Cookie header. It should really be called the AutomaticCookieHandling property. You would probably understand what was going on if the instructions said "to get custom control over cookie handling, set the AutomaticCookieHandling property to false". Instead, the instructions say to set the AllowCookies property to false. Once you've turned off automatic cookie handling, you have free access to the cookie headers on incoming and outgoing messages using the standard HTTP message properties. Here's some uninteresting code for a client showing bouncing a cookie off of a server. using ( new OperationContextScope(client.InnerChannel)) { HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); requestProperty.Headers.Add( "Cookie" , "MyCookie=value" ); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty; client.DoOperation(); MessageProperties properties = OperationContext.Current.IncomingMessageProperties; HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name]; string cookieHeader = responseProperty.Headers[HttpResponseHeader.SetCookie]; } Code on the server would look very similar with the roles of Cookie and Set-Cookie reversed. Next time: Controlling Certificate Validation Read More...
  • Custom Password Validation for HTTP

    Phil Henning has written about creating a custom username/password validator for HTTP , which is another new feature in Orcas. Like getting access to client IP addresses , creating a custom password validator is a feature added as a result of direct customer feedback. In fact, the two features were added during the same week and were among the last features we did in Orcas for messaging. Before Orcas you could only create a custom password validator if you were using message security. Read More...
More Posts Next page »

Copyright © 2006 Microsoft Corporation. All Rights Reserved. | Terms of Use | Privacy Statement | Contact Us