|
|
Browse by Tags
All Tags » Orcas » HTTP (RSS)
-
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...
|
-
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...
|
-
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...
|
-
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...
|
-
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...
|
-
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...
|
-
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...
|
-
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...
|
|
|
|