Monday, January 24, 2011

RESTful WCF JSON Service

RAVAGE

Some of my friends inquired me on creating a WCF REST service that supports JSON data format. Observing the nature of their projects, I understood that the client that’s going to utilize/consume this service is going to be a mobile client (iPhone, Andriod,…). So I just created a proof of concept and handed over the service to them. Though this is simple, configuring and hosting the service even locally leads to several exceptions that will drive us ahead of the predicted time. Hence, in order to eradicate those confusions, I explain in this post on creating a simple WCF REST service with just the required configurations and implementations.

IDE : VisualStudio 2010
Framework: .Net 4.0
Assumption: Prior knowledge in .Net and WCF.


Above specified IDE and framework is just to understand the explanations specified in this post. This is also applicable for framework “.Net 3.5” and related IDE (VS 2008).

Projects in our Solution

Project TypePurposeReferences
WCF Service LibraryHolds the DataContract and ServiceContract.System.ServiceModel.Web.dll
WCF Service ApplicationSpecifies the hosting service.Our “WCF Service Library” project reference.
Console ApplicationActs as client application to verify our service in respective data format(JSON here).Our “WCF Service Library” project reference. WCF REST Starter Kit references Microsoft.Http.dll, Microsoft.Http.Extensions.dll and Microsoft.ServiceModel.Web.dll.

WCF Service Library

As we do with simple WCf service, need to create the DataContract.

While specifying the OperationContract, we need to add the WebGet or WebInvoke attribute based on the nature of the operation/ which HTTP method we're going to use . Simply, use WebGet if your service operation wants to respond to GET method, use WebInvoke otherwise.

In order to make use of WebGet or WebInvoke attribute, first we need to add reference to System.ServiceModel.Web.dll. If the dll is not available to add reference, do verify the target framework. If it is ".NET Framework 4 Client Profile", change it to ".NET Framework 4" and then try adding reference.

Snippet 1

namespace VerifcationWcfServiceLibrary
{
    [ServiceContract]
    public interface IVerificationService
    {
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, UriTemplate = "/AuthCode/AuthReq/", Method = "PUT")]        
        AuthorizationResponse Authenticate(AuthenticationRequest request);

    }

    [DataContract]
    public class AuthenticationRequest
    {
        string userName = string.Empty;
        string authorizationCode = string.Empty;

        [DataMember]
        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }

        [DataMember]
        public string AuthorizationCode
        {
            get { return authorizationCode; }
            set { authorizationCode = value; }
        }
    }

    [DataContract]
    public class AuthorizationResponse
    {
        bool valid = false;
        string message = string.Empty;
        DateTime validToDate = DateTime.Now;
        string serverURL = string.Empty;

        [DataMember]
        public bool Valid
        {
            get { return valid; }
            set { valid = value; }
        }

        [DataMember]
        public string Message
        {
            get { return message; }
            set { message = value; }
        }

        [DataMember]
        public DateTime ValidToDate
        {
            get { return validToDate; }
            set { validToDate = value; }
        }

        [DataMember]
        public string ServerURL
        {
            get { return serverURL; }
            set { serverURL = value; }
        }
    }
}

Actually I’m trying to expose a service with a single method that uses a custom data type for input parameter(AuthenticationRequest) and another custom data type as return type(AuthorizationResponse).

WebInvoke attribute is the only new thing I’ve specified specific to REST. As you can infer from the above code, we need to explicitly specify the ResponseFormat, RequestFormat and UriTemplate. If we skip out the ResponseFormat and RequestFormat, then it’ll consider the data format as XML. UriTemplate is an important property which specifies the Uri template for the service operation, which is the access point for the service operation. Hence, whichever client going to call the service operation, needs to specify the URI of the specific operation only based on the UriTemplate.

Now create an implementation for the above defined operation contract.

Snippet 2


namespace VerifcationWcfServiceLibrary
{
    public class VerificationService : IVerificationService
    {        
        public AuthorizationResponse Authenticate(AuthenticationRequest request)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;

            AuthorizationResponse response = new AuthorizationResponse();
            response.Valid = true;
            response.ValidToDate = DateTime.Now.AddDays(7);
            response.Message = request.AuthorizationCode;
            response.ServerURL = "http://info.titodotnet.com/";

            return response;
        }

    }
}

We’re done with WCF Service Library(VerifcationWcfServiceLibrary here).

WCF Service Application

Create WCF Service(.svc) file (VerificationService.svc here).
Add reference to the project WCF Service Library(VerifcationWcfServiceLibrary).
Remove the “.svc.cs” code behind file and view the “.svc” markup.
Modify the “ServiceHost” directive as follows.

Snippet 3


<%@ ServiceHost Language="C#" Debug="true" 
    Service="VerifcationWcfServiceLibrary.VerificationService" 
    CodeBehind="VerifcationWcfServiceLibrary.VerificationService.cs" 
    Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>


As specified, Service should be the fully qualified name of our service class, CodeBehind should be the file name of our service and the Factory should be “WebServiceHostFactory” in order to use WebServiceHost to host the service.

If you miss out the Factory attribute, it’ll throw “unsupported media type” exception. Instead you can also use “WebServiceHost2Factory” of WCF REST starter kit.

Here comes the main part – configuration.

Snippet 4

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="DefaultServiceBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name="WebHttpBehavior">
        <webHttp/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <services>
    <service behaviorConfiguration="DefaultServiceBehavior" name="VerificationService">
      <endpoint behaviorConfiguration="WebHttpBehavior" binding="webHttpBinding"
        bindingConfiguration="" contract="VerifcationWcfServiceLibrary.IVerificationService"/>
    </service>
  </services>
</system.serviceModel>

We need to specify the webHttp behavior on an endpoint with webHttpBinding. Most of the people get confused while specifying the behavior, whether to use webHttp or enableWebScript. WebScriptEnablingBehavior(enableWebScript) is specially targeted for ASP.NET AJAX client. Hence, do use WebHttpBehavior(webHttp) for pure RESTful WCF service.

Console Application

The purpose of the Console Application(JsonBehaviorTestConsoleApplication here) is to validate and verify the service we created and to run our unit test cases if any. As I specified earlier since the client can be any mobile client or any other application, we need to test our services by sending and receiving object in JSON(or XML) specific format. For accessing the service, we can make use of the WCF REST starter kit, which provides the required helper functions and wrapper classes.

Make sure that you add the references that I mentioned initially in this post.
The reason for having “WCF Service Library” project reference is because, we can create .Net CLR(managed) instance for the data contract we have in “WCF Service Library” project, assign the values we needed and convert the object into JSON(or XML) specific format.

Following is the snippet for accessing the above created service operation.

Snippet 5

namespace JsonBehaviorTestConsoleApplication
{
    class JsonTestClient
    {
        static void Main(string[] args)
        {
            AuthenticationRequest request = new AuthenticationRequest();
            
            request.UserName = "test username";
            request.AuthorizationCode = "test authcode";

            using (HttpClient restClient = new HttpClient())
            {
                HttpContent paramContent = HttpContentExtensions.CreateJsonDataContract(request);
                
                Console.WriteLine("*******************AuthCode/AuthReq*******************");
                HttpResponseMessage resp = restClient.Put("http://localhost.:1517/VerificationService.svc/AuthCode/AuthReq", paramContent);
                resp.EnsureStatusIsSuccessful();
                Console.WriteLine("*******************Header*******************");
                Console.WriteLine(resp.Headers.ToString());

                Console.WriteLine("*******************Content*******************");
                var result = resp.Content.ReadAsString();
                Console.WriteLine(result);

                Console.Read();
            }
        }
    }
}

The service operation need to be called only with the UriTemplate we specified in the WebInvoke attribute using the HttpClient instance with respective method based on the Method attribute we specified earlier.

As most of might have already noticed, I’ve used “localhost.:1517”(affix dot(.) at the end of "localhost") in the above snippet, in order to capture my localhost traffic in fiddler which otherwise won’t get caught. Using Fiddler for verifying the data format and actual values passed across the wire is very common and effective.

SUMMARY

You can also bypass WCF Service Library project and create the required contracts and services in WCF Service Application itself. But make sure that you don’t add direct WCF Service Application project reference in the Console Application in order to use the data contract. Instead copy the required data contract locally in your Console Application.

REFERENCE

http://msdn.microsoft.com/en-us/library/dd203052.aspx
http://msdn.microsoft.com/en-us/library/ee391967.aspx
http://blogs.msdn.com/b/endpoint/archive/2010/01/18/automatic-and-explicit-format-selection-in-wcf-webhttp-services.aspx

No comments:

Post a Comment

Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.