Previous Articles
New Features of WCF 4.0: Part I
New Features of WCF 4.0: Part II
New Features of WCF 4.0: Part III
Introduction
Microsoft.NET 4.0 and Visual Studio.NET 2010 ships a lot of new features in their underlying technologies. In this series of articles, I want to talk about the new features in the area of Windows Communication Foundation (WCF) in order to improve the development experience, enable more communication scenario, support new WS-* standards and provide a good integration with Windows Workflow Foundation (WF).
The new features are essentially the following: simplified configuration, standard endpoints, IIS hosting without a SVC file, support for WS-Discovery, routing service, REST improvements, enhancements for the integration with WF to support workflow services, simple byte stream encoding and ETW tracing.
In this series of article, I will illustrate each feature explaining the principles and showing some examples.
REST improvements
WCF ships a lot of improvements for the development of Web services using REST approach. These features can be accessed using the WebHttpBinding binding. Many of these features were introduced in the WCF Rest Starter Kit and now they are part of Microsoft.NET framework.
Automatic help page. This is a nice feature because REST Web services have a lack of description mechanisms to provide the metadata associated with the service. When you host a service using WebServiceHost in WCF 4, a help page is generated automatically. This is done using the WebHttpBinding and WebHttpBehavior configuration by default in an environment defined by a WebServiceHost instance. The WebHttpBehavior comes with a HelpEnabled property to enable or disable the help page feature.
Let's apply this principle to the EchoService developed in previous articles. Let's create a console application to host our REST service.
Now let's add the references to the WCF libraries needed to build the solution. We need to add the System.ServiceModel.dll and System.ServiceModel.Web.dll assemblies (see Figure 1).
Figure 1
If you don't see the System.ServiceModel.Web.dll assembly from the Add Reference dialog box, you have to right-click on the console application project and select the Properties option from the context menu. When the Properties page appears, from the Application tab go to the Target frameworks field and change to the .NET Framework 4 option (see Figure 2).
Figure 2
Next step is to define the service contract. We're going to use the WCF artifacts and extensions to REST Web services. In this case, we're going to call the EchoService using traditional HTTP Get and HTTP Post methods (two operations for each HTTP method) and the messages will be encapsulated using plain old HTTP ways (see the Listing 1).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WCF_NewFeatures
{
[ServiceContract]
public interface IEchoService
{
[OperationContract]
[WebGet]
string EchoGetMethod(String message);
[OperationContract]
[WebInvoke]
string EchoPostMethod(String message);
}
}
Listing 1
Next step is to realize the contract with a concrete service (see the Listing 2). In this case, the outgoing messages of the service will be in plain format, although we can use different format for the message such as XML, Json, etc.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_NewFeatures
{
public class EchoService : IEchoService
{
#region IEchoService Members
public string EchoGetMethod(string message)
{
return "EchoGetMethod invoked using HTTP Get method. Message is " + message;
}
public string EchoPostMethod(string message)
{
return "EchoGetMethod invoked using HTTP Post method. Message is " + message;
}
#endregion
}
}
Listing 2
Next step is to define the WCF artifacts in order to host the service in the console application (see Listing 3).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace WCF_NewFeatures
{
class Program
{
static void Main(string[] args)
{
WebServiceHost serviceHost = new WebServiceHost(typeof(WCF_NewFeatures.EchoService),new Uri("http://localhost:8080/Services/EchoService"));
serviceHost.Open();
System.Console.WriteLine("The EchoService has started");
foreach (ServiceEndpoint se in serviceHost.Description.Endpoints)
{
System.Console.WriteLine("Address:{0}, Binding:{1}, Contract:{2}", se.Address, se.Binding.Name, se.Contract.Name);
}
System.Console.WriteLine("Please, press any key to finish ...");
System.Console.ReadLine();
serviceHost.Close();
}
}
}
Listing 3
When you configure a WebServiceHost instance to host a service, it automatically configures your service with the WebHttpBehavior and adds a default HTTP endpoint configured with the WebHttpBinding at the base address. The only thing to do is to set the HelpEnabled property to true of the WebHttpBehavior (see Listing 4).
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="WCF_NewFeatures.EchoService">
<endpoint address="" behaviorConfiguration="WCF_NewFeatures.EchoServiceBehavior" binding="webHttpBinding" contract="WCF_NewFeatures.IEchoService">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="WCF_NewFeatures.EchoServiceBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
Listing 4
Now you can see the help page by browsing to the service's base address and appending the help string to the end of the url (see Listing 4).
Figure 3
If you see the help page, you realize that you can test the EchoGetMethod operation of the service by sending a request using HTTP GET method and passing information in the message parameter. This can be easily done using the browser and the following url http://localhost:8080/Services/EchoService/EchoGetMethod?message=test and see the result in the Figure 4.
Figure 4
Let's see how you can configure the Help Page feature using code without the need of the configuration file (see Listing 5).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace WCF_NewFeatures
{
class Program
{
static void Main(string[] args)
{
WebServiceHost serviceHost = new WebServiceHost(typeof(WCF_NewFeatures.EchoService),new Uri("http://localhost:8080/Services/EchoService"));
serviceHost.AddServiceEndpoint(typeof(WCF_NewFeatures.IEchoService), new WebHttpBinding(),"");
serviceHost.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true});
serviceHost.Open();
System.Console.WriteLine("The EchoService has started");
foreach (ServiceEndpoint se in serviceHost.Description.Endpoints)
{
System.Console.WriteLine("Address:{0}, Binding:{1}, Contract:{2}", se.Address, se.Binding.Name, se.Contract.Name);
}
System.Console.WriteLine("Please, press any key to finish ...");
System.Console.ReadLine();
serviceHost.Close();
}
}
}
Listing 5
You can control the REST binding such as message formats and uri template (to apply to REST uri style) using the properties of the WebGet and WebInvoke attribute (see Table 1).
Property Name |
Type |
Possible Value |
Description |
BodyStyle |
WebMessageBodyStyle |
Bare Wrapped WrappedRequest WrappedResponse |
Controls the aspects of the message envelop. |
RequestFormat |
WebMessageFormat |
Json XML |
Specifies the message format for the request |
ResponseFormat |
WebMessageFormat |
Json XML |
Specifies the message format for the response |
UriTemplate |
String |
Any string representing a valid uri |
The template that matches against the incoming request |
Table 1
Let me demonstrate how to describe the shape of the URIs to which the EchoService will respond. For example, let's change the request URI for the service by adding the following pattern GetEcho/{message} for the GET method, and PostEcho/{message} for the POST method where the message is the parameter. Now you can access to the service and pass the parameters in this way, http://localhost:8080/Services/EchoService/getecho/mymessage (see Figure 5).
Figure 5
Let me demonstrate how to support different message format, for example, XML and Json. Now, we're going to change the interface to expose two operations for processing messages coming using HTTP Get method and sending the response either by XML or Json depending on the request URI as shown in the previous example (see Listing 6).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WCF_NewFeatures
{
[ServiceContract]
public interface IEchoService
{
[OperationContract]
[WebGet(UriTemplate="GetEcho/json/{message}", ResponseFormat=WebMessageFormat.Json)]
string EchoGetMethodJson(String message);
[OperationContract]
[WebGet(UriTemplate = "GetEcho/xml/{message}", ResponseFormat = WebMessageFormat.Xml)]
string EchoGetMethodXml(String message);
[OperationContract]
[WebInvoke(UriTemplate = "PostEcho/{message}")]
string EchoPostMethod(String message);
}
}
Listing 6
And the implementation of the contract is shown in the service of the Listing 7.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_NewFeatures
{
public class EchoService : IEchoService
{
#region IEchoService Members
public string EchoGetMethodJson(string message)
{
return "EchoGetMethodJson invoked using HTTP Get method. Message is (Json format) " + message;
}
public string EchoGetMethodXml(string message)
{
return "EchoGetMethodXml invoked using HTTP Get method. Message is (Xml format) " + message;
}
public string EchoPostMethod(string message)
{
return "EchoGetMethod invoked using HTTP Post method. Message is " + message;
}
#endregion
}
}
Listing 7
Going to the Help Page for the service, you will find the description of the service and the way to access to this (see Figure 6).
Figure 6
So, you can access to the service using the HTTP Get method and receiving the response formatted using Json by the following URI http://localhost:8080/Services/EchoService/getecho/json/mymessage .
In WCF 4.0 you can also access to the request and message format at runtime. Let's create a generic getecho operation passing as parameter the format of the outgoing message and processing this information at runtime.
Let's redefine the service contract again as shown in the Listing 8.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WCF_NewFeatures
{
[ServiceContract]
public interface IEchoService
{
[OperationContract]
[WebGet(UriTemplate="GetEcho/{message}")]
string EchoGetMethod(String message);
[OperationContract]
[WebInvoke(UriTemplate = "PostEcho/{message}")]
string EchoPostMethod(String message);
}
}
Listing 8
And the implementation of the contract is shown in the Listing 9.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCF_NewFeatures
{
public class EchoService : IEchoService
{
#region IEchoService Members
public string EchoGetMethod(string message)
{
string outgoingMessageFormat = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
if (!String.IsNullOrEmpty(outgoingMessageFormat))
{
if (outgoingMessageFormat.Equals("json",StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
else
if (outgoingMessageFormat.Equals("xml", StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
}
return "EchoGetMethodJson invoked using HTTP Get method. Message is (Json format) " + message;
}
public string EchoPostMethod(string message)
{
return "EchoGetMethod invoked using HTTP Post method. Message is " + message;
}
#endregion
}
}
Listing 9
Now if you access to the service using the following URI http://localhost:8080/Services/EchoService/getecho/mymessage?format=xml, you will receive a the response in XML format, and if you request the service using the following URI http://localhost:8080/Services/EchoService/getecho/mymessage?format=json, you will receive the response in Json format.
Another good feature of WCF 4.0 to support REST Web services is the error handling support in order to build more robust applications and avoid constructing manually the HTTP response with error message.
Now we can use a new class for error handling: WebFaultException<T>. For example, if you want to include error handling in the previous example in order to report an unsupported format, you need to include the code shown in the Listing 10 highlighted in yellow.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Net;
namespace WCF_NewFeatures
{
public class EchoService : IEchoService
{
#region IEchoService Members
public string EchoGetMethod(string message)
{
string outgoingMessageFormat = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
if (!String.IsNullOrEmpty(outgoingMessageFormat))
{
if (outgoingMessageFormat.Equals("json",StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
else
if (outgoingMessageFormat.Equals("xml", StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
else
{
throw new WebFaultException<string>("Unsupported format "+ outgoingMessageFormat, HttpStatusCode.BadRequest);
}
}
return "EchoGetMethodJson invoked using HTTP Get method. Message is (Json format) " + message;
}
public string EchoPostMethod(string message)
{
return "EchoGetMethod invoked using HTTP Post method. Message is " + message;
}
#endregion
}
}
Listing 10
Another good feature in WCF to support REST Web services is the HTTP Caching. WCF comes with a simple model to implement caching through the [AspNetCacheProfile] attribute that you can apply to operations supported by HTTP Get method. This attribute enables specifying an ASP.NET caching profile name for each operation and behind the scenes there's a cache inspector taking care of the handling of the underlying caching implementation.
Conclusion
In this series of article, I've explained the new features of WCF 4.0 through concepts and examples.
Next Articles
New Features of WCF 4.0: Part V