Implementing RESTful services in .NET 4.5

In this section, we will implement a RESTful service using the WCF. The WCF is a framework based on the Service Oriented Architecture (SOA) that is used to design distributed applications, which are applications that have the capability to intercommunicate. We will explore more about the WCF in Chapter 3, Working with Restful Services.

The UserNamePasswordValidator class

The UserNamePasswordValidator class has been introduced in the newer versions of the WCF. You can use this class to design and implement your own custom validators for validating a user's credentials.

The UserNamePasswordValidator class in the System.IdentityModel.Selectors namespace can be used to validate user credentials in WCF 4.5. You can create your own custom validator by simply extending the UserNamePasswordValidator class, and then overriding the Validate method, as shown in the following code snippet:

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.ServiceModel;
namespace Packt
{
  public class PacktValidator : UserNamePasswordValidator
  {
    public override void Validate(String userName, String password)
    {
      if (!userName.Equals("joydip")) || !password.Equals("joydip1@3"))
      {
        throw new SecurityTokenException("User Name and/or Password incorrect...!");
      }
    }
  }
}

Then, you can configure the validator you just created in the configuration file, as shown in the following code:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <bindings>
        <wsHttpBinding>
          <binding name="PacktAuthentication">
            <security mode="Transport">
              <transport clientCredentialType="Basic" />
            </security>
          </binding>
        </wsHttpBinding>
      </bindings>

      <behaviors>
        <serviceBehaviors>
          <behavior name="PacktValidator.ServiceBehavior">
            <serviceCredentials>
              <userNameAuthentication
              userNamePasswordValidationMode="Custom"customUserNamePasswordValidatorType="Packt.PacktValidator, Packt"/>
            </serviceCredentials>
          </behavior>
        </serviceBehaviors>
      </behaviors>
    </services>
  </system.serviceModel>
</configuration>

The new enhancements in WCF 4.5 include the following:

  • Simplified configuration
  • Standard endpoints
  • Discovery
  • Simplified IIS Hosting
  • REST improvements
  • Workflow services
  • Routing service
  • Automatic Help page

Simplified configuration

WCF 4.5 starts with the default configuration model. The configuration in WCF 4.5 is much simpler in comparison with its earlier counterparts. In WCF 3.x, you needed to specify the endpoints, behavior, and so on for the service host. With WCF 4.5, default endpoints, binding information, and behavior are provided. In essence, WCF 4.0 eliminates the need of any WCF configuration when you are implementing a particular WCF service.

There are a few standard endpoints and default binding/behavior configurations that are created for any WCF service in WCF 4.5. This makes it easy to get started with the WCF, because the tedious configuration details of WCF 3.x are no longer required.

Consider the following WCF service:

using System;
using System.ServiceModel;
namespace PacktService
{
  [ServiceContract]
  public interface ITestService
  {
    [OperationContract]
    String DisplayMessage();
  }

  public class TestService : ITestService
  {
    public String DisplayMessage()
    {
      return "Hello World!";
    }
  }
}

In WCF 4.5, you can use ServiceHost to host the WCF service without the need for any configuration information whatsoever. The following code is all that you need to host your WCF service and display the address, binding, and contract information:

using System.ServiceModel;
using System;
using System.ServiceModel.Description;
namespace PacktClient
{
  class Program
  {
    static void Main(string[] args)
    {
      ServiceHost serviceHost = new ServiceHost(typeof(PacktService.TestService));
      serviceHost.AddServiceEndpoint(typeof(PacktService.TestService), new BasicHttpBinding(),"http://localhost:1607/TestService.svc");
      serviceHost.Open();
      foreach (ServiceEndpoint serviceEndpoint in serviceHost.Description.Endpoints)
      Console.WriteLine("Address: {0}, Binding: {1}, Contract: {2}", serviceEndpoint.Address, serviceEndpoint.Binding.Name, serviceEndpoint.Contract.Name);
      Console.ReadLine();
      serviceHost.Close();
    }
  }
}

The following code is an example of all the configuration information that you need to specify to consume your service in WCF 4.5:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled ="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Note that the BasicHttpBinding binding used is by default. If you want to choose a more secure binding, such as WSHttpBinding, you can change the binding information by using the following code snippet:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled ="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme ="http"/>
    </protocolMapping>
  </system.serviceModel>
</configuration>

Standard endpoints

Standard endpoints are preconfigured endpoints in the WCF Framework 4.5. You can always re-use them, but they don't generally change.

You can use any of the previous endpoints by referencing them in the <configuration> element using the endpoint name. An example of the same is given as follows:

<configuration>
  <system.serviceModel>
    <services>
      <service name="PacktService">
        <endpoint kind="basicHttpBinding" contract="IMyService"/>
        <endpoint kind="mexEndpoint" address="mex" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Discovery

There are two modes of operation. They are given as follows:

  • Ad-Hoc mode: In this mode, there is no centralized server, and all service announcements and client requests are sent in a multicast manner.
  • Managed mode: In this mode, you have a centralized server. Such a server is known as a discovery proxy, where the services are published centrally and the clients who need to consume such published services connect to this to retrieve the necessary information.

You can just add the standard udpDiscoveryEndpoint endpoint and also enable the <serviceDiscovery> behavior to enable service discovery in the Ad-hoc mode. The following code is an example of this:

<configuration>
  <system.serviceModel>
    <services>
      <service name="TestService">
        <endpoint binding="wsHttpBinding" contract="ITestService" />
        <!-- add a standard UDP discovery endpoint-->
        <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TestService.MyServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
            <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to falsebefore deployment to avoid disclosing exception information -->
            <serviceDebug includeExceptionDetailInFaults="false"/>
          <serviceDiscovery /> 
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Note that in the previous code snippet, a new endpoint has been added to discover the service. Also, the ServiceDiscovery behavior has been added. You can use the DiscoveryClient class to discover your service and invoke one of its methods.

You must create an instance of the DiscoveryClient class and pass UdpDiscoveryEndPoint to the constructor of this class as a parameter to discover the service. Once the endpoint has been discovered, the discovered endpoint address can then be used to invoke the service. The following code snippet illustrates this:

using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
namespace PacktConsoleApplication
{
  class Program
  {
    static void Main(string[] args)
    {
      DiscoveryClient discoverclient = new DiscoveryClient(new UdpDiscoveryEndpoint());
      FindResponse findResponse = discoverclient.Find(new FindCriteria(typeof(ITestService)));
      EndpointAddress endpointAddress = findResponse.Endpoints[0].Address;
      MyServiceClient serviceClient = new MyServiceClient(new  WSHttpBinding(), endpointAddress);
      Console.WriteLine(serviceClient.DisplayMessage());
    }
  }
}

WCF 4.5 also enables you to configure services to announce their endpoints as soon as they are started. The following code shows how you can configure your service to announce endpoints at the time it starts:

<configuration>
  <system.serviceModel>
    <services>
      <service name="TestService">
        <endpoint binding="wsHttpBinding" contract="ITestService"/>
        <endpoint kind="udpDiscoveryEndpoint"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceDiscovery>
            <announcementEndpoints>
              <endpoint kind="udpAnnouncementEndpoint"/>
            </announcementEndpoints>
          </serviceDiscovery>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Simplified IIS hosting

Hosting of WCF 4.5 applications on IIS has now become much easier. An example of a simple WCF service is given as follows:

<!-- PacktService.svc -->
<%@ ServiceHost Language="C#" Debug="true" Service=" PacktService
  CodeBehind="~/App_Code/PacktService.cs" %>
[ServiceContract]
public class PacktService
{
  [OperationContract]
  public string GetMessage()
  {
    return "This is a test service.";
  }
}

You can then enable service metadata for the service in the application's web.config configuration file, as shown in the following code snippet:

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

You can also define virtual service activation endpoints for your WCF 4.5 services in your application's web.config configuration file. By doing so, you can activate your WCF service without the need for .svc files. You need to specify the following configuration in your application's web.config configuration file to activate your service without the need for a .svc file:

<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add relativeAddress="PacktService.svc" service="PacktService"/>
      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>

Improvements in REST

WCF 4.5 comes with improved support for REST-based features. You now have support for an automatic help page that describes the REST-based services available for the service consumers or clients. This feature is turned on by default, but you can also manually configure it, as shown in the following code listing:

<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <behaviors>
      <endpointBehaviors>
        <behavior name="PacktTestHelpBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="PacktSampleWCFService">
        <endpoint behaviorConfiguration="PacktTestHelpBehavior"binding="webHttpBinding"contract="PacktSampleWCFService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

WCF 4.5 also comes with support for HTTP caching, which is done by using the AspNetCacheProfile attribute. Note that the AspNetCacheProfile support actually uses the standard ASP.NET output caching mechanism to provide you with caching features in your WCF service.

To use this attribute, you should add a reference to the System.ServiceModel.Web.Caching namespace. You can apply this attribute in a WebGet operation and specify the cache duration of your choice. The following code snippet can be used in your service contract method to make use of this feature:

using System.ServiceModel.Web.Caching;
[OperationContract]
[WebGet] 
[AspNetCacheProfile("PacktCache")]
String GetProductName();

Accordingly, you should set the cache profile in your application's web.config file, as shown here:

<caching>
  <outputCacheSettings>
    <outputCacheProfiles>
      <add name="PacktCache" duration="60" varyByParam="format"/>
    </outputCacheProfiles>
  </outputCacheSettings>
</caching>

Implementing a RESTful Service using WCF 4.5

In this section, we will implement our first RESTful service using WCF 4.5. To do this, we need to follow these steps:

  1. Create a WCF Service
  2. Make the service RESTful
  3. Specify binding information
  4. Host the RESTful service
  5. Consume the RESTful service

Creating a WCF service

A typical WCF implementation would have a WCF service and a WCF client. The WCF client would consume the services provided by the WCF service.

Note that a WCF service contains:

  • A Service class
  • A hosting environment
  • One or more endpoints

The Service class is written using a language targeted at the managed environment of the .NET CLR. In essence, the Service class can be written in any .NET language of your choice (we use C# throughout this book). The hosting environment is the environment inside the context of which the WCF service would execute. The endpoints enable the clients or the service consumers to access the WCF service.

There are two templates from which you can choose to create the WCF services: the Visual Studio 2013 WCF service library template and the Visual Studio service application template.

Let's first use the Visual Studio WCF service library template to create a WCF service. To do this, follow these steps:

  1. Open Visual Studio 2013 IDE
  2. Navigate to File | New | Project
  3. Select WCF Service Application from the list of templates displayed, as shown in the following screenshot:
    Creating a WCF service

    Creating a WCF Service Application Project

  4. Provide a name for your project, and click on OK to save.

A WCF service application project is created. At first glance, the Service class looks like the following code:

using System;
namespace MyDataService
{
  // NOTE: You can use the "Rename" command on the "Refactor" menu to change 
  // the class name "Service1" in code, svc and config file together.
  public class Service1 : IService1
  {
    public string GetData(int value)
    {
      return string.Format("You entered: {0}", value);
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
      if (composite == null)
      {
        throw new ArgumentNullException("composite");
      }
      if (composite.BoolValue)
      {
        composite.StringValue += "Suffix";
      }
      return composite;
    }
  }
}

The Service class in the previous code snippet implements the IService1 interface, as shown here:

using System.Runtime.Serialization;
using System.ServiceModel;
namespace MyDataService
{
  // NOTE: You can use the "Rename" command on the "Refactor" menu 
  // to change the interface name "IService1" in both code 
  // and config file together.
  [ServiceContract]
  public interface IService1
  {
    [OperationContract]
    string GetData(int value);
    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
    // TODO: Add your service operations here
  }
  // Use a data contract as illustrated in the sample below 
  // to add composite types to service operations.
  [DataContract]
  public class CompositeType
  {
    bool boolValue = true;
    string stringValue = "Hello ";
    [DataMember]
    public bool BoolValue
    {
      get { return boolValue; }
      set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
      get { return stringValue; }
      set { stringValue = value; }
    }
  }
}

Consider the following WCF service:

using System.ServiceModel;
namespace Test
{
  [ServiceContract]
  public interface ITestService
  {
    [OperationContract]
    String GetMessage();
  }
  public class TestService : ITestService
  {
    public string GetMessage()
    {
      return "Hello World!";
    }
  }
}

Tip

Note the usage of the OperationContract attribute in the previous code snippet. This attribute is used to specify that a particular operation is exposed to calls by clients.

To host this WCF service, you can write the following code:

using System;
using System.ServiceModel;
using Test;
namespace MyApp
{
  class Program
  {
    static void Main(string[] args)
    {
      using (ServiceHost serviceHost = new ServiceHost(typeof(TestService)))
      {
        serviceHost.Open();
        Console.WriteLine("WCF Service has started...");
        Console.ReadLine();
        serviceHost.Close();
      }
      Console.WriteLine("The WCF Service has stopped...");
    }
  }
}

Making the service RESTful

When designing a RESTful service, you need to know the resources, the URIs that would be used to map those resources, and the HTTP verbs that should be supported by the resources.

In a typical WCF-REST based service, you would need the WebGet attribute, apart from the OperationContract attribute you use to expose the operations of the service. The WebGet attribute belongs to the System.ServiceModel.Web namespace. A typical WebGet attribute for HTTP-GET operation is shown as follows:

[WebGet(UriTemplate = 
"/payroll/getemployees.xml",BodyStyle = WebMessageBodyStyle.Bare,RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml)]

The UriTemplate parameter of the WebGet attribute is used to define the URL format for accessing the service operation. The RequestFormat and ResponseFormat arguments are a part of the WebMessageFormat enumeration and can have one of the two possible values: Xml and Json. The following code is how a typical WebGet attribute for a HTTP-POST operation looks:

[GetOperationContract]
[WebInvoke(UriTemplate = 
"/payroll/updateemployee.xml?
     employeecode={code}",Method = "POST",BodyStyle = WebMessageBodyStyle.Bare,RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml)]

To make the service we created earlier in this chapter RESTful, just change the contract ITest and specify the WebGet attribute, as shown in the following code snippet:

[ServiceContract]
[WebGet()]
  public interface ITestService
  {
    [OperationContract]
    String GetMessage();
  }

Specifying the binding information

Now that we have created the service contract and the service, along with the operations that the service would expose, we need to specify the binding information for the service so that it can be assessable by service consumers or clients. In order for a WCF service to be accessed by the clients, the service should expose endpoints. An endpoint denotes the address, binding, and contract information for the service. To specify the binding information for the service, open the App.Config file and write the following code inside the <system.serviceModel> tag:

<system.serviceModel>
  <bindings>        
  </bindings>
  <services>
    <service name ="Test.TestService" behaviorConfiguration="Default">
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8080/Test"/>
        </baseAddresses>
      </host>
      <endpointaddress=""binding ="basicHttpBinding"contract="Test.ITestService" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="Default">
        <serviceMetadata httpGetEnabled="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

In our example, we are using a RESTful service. So, we need to use the webHttpBinding class, as shown here:

<service name ="Test.TestService" behaviorConfiguration="Default">
  <host>
    <baseAddresses>
     <add baseAddress="http://localhost:8080/Test"/>
    </baseAddresses>
  </host>
  <endpointaddress=""binding ="webHttpBinding"contract="Test.TestService" />
</service>

Hosting the RESTful WCF service

There are many ways in which a WCF service can be hosted. For example, you can host your WCF service in the IIS server, or by using the Windows Activation Service (WAS). To host your WCF service in IIS, you must create a virtual directory, and make it point to the directory where your service is located. Note that the WCF services hosted in IIS can be accessed using SOAP over HTTP.

What you can specify in the App.Config file in the hosting application to access your WCF service hosted in IIS is as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled ="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme ="http"/>
    </protocolMapping>
  </system.serviceModel>
</configuration>

Hosting the service inside the console application

You can also host your WCF service programmatically if the endpoints are properly defined. To host the RESTful WCF service we created earlier, you should use WebServiceHost, as shown here:

using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using Test;
namespace MyApp
{
  class Program
  {
    static void Main(string[] args)
    {
      using (WebServiceHost serviceHost = new WebServiceHost(typeof(TestService)))
      {
        serviceHost.Open();
        Console.WriteLine("WCF Service has started...");
        Console.ReadLine();
        serviceHost.Close();
      }
      Console.WriteLine("The WCF Service has stopped...");
    }
  }
}

Returning JSON data

You can return data in JavaScript Object Notation (JSON) format from your REST-based WCF service. The following code snippet illustrates how you can return data from your RESTful service in a JSON format by setting the attributes:

using System.Xml;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
namespace Test
{
  [ServiceContract]
  public interface ITestService
  {
    [OperationContract]
    [WebGet(UriTemplate = "Test")]
    Message GetMessage();
  }
  public class TestService : ITestService
  {
    public Message GetMessage()
    {
      StringBuilder stringBuilder = new StringBuilder();
      using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
      {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("Message");
        xmlWriter.WriteAttributeString("type", "Name");
        xmlWriter.WriteString("Joydip");
        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndDocument();
      }
      TextReader textReader = new StringReader(stringBuilder.ToString());
      XmlReader xmlReader = XmlReader.Create(textReader);
      return Message.CreateMessage(MessageVersion.None, "", xmlReader);
    }
  }
}

Note that the WebGet attribute in the WCF is actually a part of the HTTP programming model for the WCF. This attribute is used to invoke the WCF service using the HTTP URI. In essence, the WebGet attribute is used to specify that the service would respond to the HTTP GET requests.

The WebGet attribute accepts one of the following four parameters:

  • BodyStyle: This is used to specify whether the requests or responses should be wrapped
  • RequestFormat: This is used to format the request messages
  • ResponseFormat: This is used to format the response messages
  • UriTemplate: This is used to specify the URI template for the HTTP requests for a service operation

The previous code snippet used an XmlWriter instance to create a message in XML format. And, here is what the returned JSON data would look like:

{
"Message": [{ "Name":"Joydip"},]
}

Consuming the RESTful Service

To consume the RESTful service we created earlier in this section, you would need to create a client application (ASP.NET or WPF), add a service reference to the service we created in the earlier sections, and then use the service reference instance to invoke the service methods. We will discuss this in detail in Chapter 3, Working with Restful Services and Chapter 4, Consuming RESTful Services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset