Friday 30 September 2011

WcfWebApi REST JSON Services

Overview

Here is an example of using WcfWebApi to produce a RESTful JSON data service.  We have three areas to consider:

  • Our data (model) is a simple Contact class
  • Our service is exposed via IContractService interface and implemented as ContractService
  • Our data store (repository) is exposed via IContactRepository interface and implemented as ContractRepository

diagram

RESTful Interface

Data Operation HTTP Verb Has Side Effects? Idempotent? Example Uri
Read All GET No Yes http://localhost:8000/contacts
Read Single GET No Yes http://localhost:8000/contacts/1
Insert POST Yes No http://localhost:8000/contacts
Update PUT Yes Yes http://localhost:8000/contacts
Delete DELETE Yes Yes http://localhost:8000/contacts/3

HTTP Status (Return) Codes

Http Code Description Examples
200 OK Successful (read, update or delete)
201 CREATED Successful (insert)
400 BAD REQUEST Passing id as “X” when an integer is expected
404 NOT FOUND Item not found (based on id)
500 SERVER ERROR Exception thrown within service on server with the message contained within response body.

Service Interface

[ServiceContract]
public interface IContactService
{
[OperationContract]
[WebGet(UriTemplate = "")]
HttpResponseMessage GetAll();

[OperationContract]
[WebGet(UriTemplate = "{id}")]
HttpResponseMessage Get(string id);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "POST")]
HttpResponseMessage Insert(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "", Method = "PUT")]
HttpResponseMessage Update(Contact contact);

[OperationContract]
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
HttpResponseMessage Delete(string id);
}

Service Implementation

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using Microsoft.ApplicationServer.Http.Dispatcher;
using RestDemo.Data;
using RestDemo.Service.Helpers;
using RestDemo.Service.Repositories;

namespace RestDemo.Service
{
public class ContactService : IContactService
{
private const string ID_MUST_BE_INTEGER = "Id must be an integer";
private const string ITEM_NOT_FOUND = "Item not found";
private readonly IContactRepository _repo;

public ContactService() : this(new ContactRepository())
{
}

public ContactService(IContactRepository repository)
{
_repo = repository;
}

#region IContactService Members

public HttpResponseMessage GetAll()
{
IEnumerable<Contact> contacts;
DataOperationResponse dataResponse = _repo.GetAll(out contacts);
HttpContent httpContent = GetHttpContent(contacts);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Get(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

Contact contact;
DataOperationResponse dataResponse = _repo.Get(intId, out contact);
HttpContent httpContent = GetHttpContent(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse, httpContent);
return httpResponse;
}

public HttpResponseMessage Insert(Contact contact)
{
DataOperationResponse dataResponse = _repo.Insert(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Update(Contact contact)
{
DataOperationResponse dataResponse = _repo.Update(contact);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

public HttpResponseMessage Delete(string id)
{
int intId;
if (!Int32.TryParse(id, out intId))
return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(ID_MUST_BE_INTEGER) };

DataOperationResponse dataResponse = _repo.Delete(intId);
HttpResponseMessage httpResponse = SetupHttpResponse(dataResponse);
return httpResponse;
}

#endregion

private static HttpContent GetHttpContent(int id)
{
HttpContent httpContent = new StringContent(Convertors.ToJson(id).ToString(), Encoding.UTF8, MediaTypes.JSON);
return httpContent;
}

private static HttpContent GetHttpContent(Contact contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpContent GetHttpContent(IEnumerable<Contact> contact)
{
HttpContent httpContent = contact
!= null
? new StringContent(Convertors.ToJson(contact).ToString(), Encoding.UTF8,
MediaTypes.JSON)
: null;
return httpContent;
}

private static HttpResponseMessage SetupHttpResponse(DataOperationResponse dataResponse, HttpContent content = null)
{
var response = new HttpResponseMessage();

switch (dataResponse.Status)
{
case DataOperationStatus.Success:

if (dataResponse.Type == OperationType.Insert)
{
response.StatusCode = HttpStatusCode.Created;
response.Content = GetHttpContent(dataResponse.InsertedId);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(30));
}
else
{
response.StatusCode = HttpStatusCode.OK;

if (content != null)
response.Content = content;
}

break;

case DataOperationStatus.NotFound:

// REST best practise on delete is to return OK if resource
// cannot be found but here we are explicitly returning Not Found
response.StatusCode = HttpStatusCode.NotFound;
response.Content = new StringContent(ITEM_NOT_FOUND);
break;

case DataOperationStatus.Exception:
response.StatusCode = HttpStatusCode.InternalServerError;
response.Content = new StringContent(dataResponse.Exception.Message);
break;
}

return response;
}

/* You might also want set the ocation header:
* response.Headers.Location = new Uri("Contacts/" + item.Id, UriKind.Relative);
*/
}
}

Service Unit Tests


Service-Unit-Tests


Hosting The Service in an ASP.NET Web Application

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var webApiConfig = new WebApiConfiguration {EnableTestClient = true};
RouteTable.Routes.SetDefaultHttpConfiguration(webApiConfig);
RouteTable.Routes.MapServiceRoute<ContactService>("Contacts");
}
}

Alternatively…. Console Hosting


The source code also contains an example of hosting within a client application:

private const string CONTACTS_URI = "http://localhost:8010/contacts";

private static void Main(string[] args)
{
using (var host = new HttpServiceHost(typeof (ContactService), CONTACTS_URI))
{
host.Open();

ExitOnReadKey();
}

private static void ExitOnReadKey()
{
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}

Client Access to the REST service interface from a Console App

private static void HttpGetAll()
{
var client = new HttpClient();
HttpResponseMessage response = client.Get(CONTACTS_URI);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPost()
{
var client = new HttpClient();
HttpContent jsonContactNew = new StringContent("{\"Id\":0,\"Name\":\"Edward\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Post(CONTACTS_URI, jsonContactNew);
Console.WriteLine("POST (INSERT CONTACT): {0} RESPONSE: {1} \n{2}\n", CONTACTS_URI, response.StatusCode, response.Content.ReadAsString());
}

private static void HttpPut()
{
var client = new HttpClient();
HttpContent jsonContactUpdated = new StringContent("{\"Id\":2,\"Name\":\"Ziggy\"}", Encoding.UTF8, MediaTypes.JSON);
HttpResponseMessage response = client.Put(CONTACTS_URI, jsonContactUpdated);
Console.WriteLine("PUT (UPDATE CONTACT ID 2): {0} RESPONSE: {1}\n", CONTACTS_URI, response.StatusCode);
}

private static void HttpDelete()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/3";
HttpResponseMessage response = client.Delete(uri);
Console.WriteLine("DELETE (CONTACT ID 3): {0} RESPONSE: {1} \n", uri, response.StatusCode);
}

private static void HttpGet()
{
var client = new HttpClient();
const string uri = CONTACTS_URI + "/1";
HttpResponseMessage response = client.Get(uri);
Console.WriteLine("GET: {0} RESPONSE: {1} \n{2}\n", uri, response.StatusCode, response.Content.ReadAsString());
}

// Some methods worth knowing:
// 1. When expecting JSON in response body you can set the following header in the request:
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypes.JSON));
//
// 2. When deserializing JSON single item within response body use the following:
// var contact = response.Content.ReadAs<Contact>();
//
// 3. When deserializing JSON collection within response body use the following:
// var contacts = response.Content.ReadAs<Contact[]>();

Output


ScreenShot163


Monitoring


HTTP


You can use Fiddler to monitor all HTTP requests and responses.  The screenshot below shows Fiddler on the left hand side picking up all requests and responses from the WCF Web API Test Client (explained within Debugging below) on the right hand side.


Fiddler-Monitoring


Debugging


Configure the Firewall (if required)


Make sure you punch a hole in your firewall before testing (or change the port from 8010 to 80 in the source code). 


On Windows 7 > Control Panel > Windows Firewall > Allow a Program or Feature through the Firewall


ScreenShot152


Fiddler


We are now ready to open Fiddler and use the Request Builder tab to enter your URI, set the HTTP verb (defaults to GET) and execute your request.  On the left hand side you’ll see the HTTP response and headers, in this case the result was 200 (OK):


Fiddler-GET-example1


Then switch to the Inspectors tab to view the response body containing our data.


Fiddler-GET-example2


For PUT and POST requests you must add the following to the request headers:


Content-Type: application/json; charset=utf-8


Fiddler-POST-exampleFiddler-PUT-example


WCF Web API Test Client


We switched this new Microsoft Tool on in the Global.asax page with “EnableTestClient = true” which automatically routes:



In our case this would be http://localhost:8010/Contacts/Test:


ScreenShot153ScreenShot156ScreenShot157ScreenShot158ScreenShot159


 


Full source code:  http://stevenhollidge.com/blog-source-code/RestDemo.zip

Thursday 29 September 2011

NServiceBus Prototypes

There's an excellent new Modelling tool available from NServiceBus that lets you model a messaging system architecture then code generate all the relevant projects, references, contracts, configs etc.

nservicebus-modeling-tool_thumb1

Here's a short video from NServiceBus that shows you how easy it is to use the Visual Studio Addin:

http://vimeo.com/29659143

Wednesday 28 September 2011

ØMQ (Zero MQ)

ZeroMQ is a socket library that allows developers to create distributed, concurrent, queue based messaging systems with high availability.  One of the many benefits is the abstraction of dealing with sockets at a queue and atomic message level.

It’s really simple to use, here is an example:

Client

namespace Client
{
using System;
using System.Text;

class Program
{
static void Main()
{
using (var ctx = new ZMQ.Context(1))
{
var socket = ctx.Socket(ZMQ.REQ);
socket.Connect("tcp://localhost:5555");

while (true)
{
socket.Send(Encoding.ASCII.GetBytes("Hello"));

byte[] message;
socket.Recv(out message);

Console.WriteLine(Encoding.ASCII.GetString(message));
}
}
}
}
}

Server

namespace Server
{
using System;
using System.Text;

class Program
{
static void Main()
{
using (var ctx = new ZMQ.Context(1))
{
var socket = ctx.Socket(ZMQ.REP);
socket.Bind("tcp://*:5555");

while (true)
{
byte[] message;
socket.Recv(out message);

Console.WriteLine(Encoding.ASCII.GetString(message));

socket.Send(Encoding.ASCII.GetBytes("World"));
}
}
}
}
}

This sample was taken from the internet but you can download a snapshot of the source here:


http://stevenhollidge.com/blog-source-code/ZeroMqDemo.zip

Monday 26 September 2011

Silverlight Testing Automation Tool

Unit testing out of the box with Silverlight is a real pain.  Luckily for us StatLight makes its a breeze and also provides continuous integration with Visual Studio and TeamCity.

My code below shows a nice example of an application using the MVVM Light framework.

How Does It Work?

StatLight is a console application that creates an in memory web server. It starts up a web browser that will request from the web server a page containing your test xap. By executing all the tests in the browser and communicating test results back to the web server. The console now has the ability to publish those results in manners not allowed in the Silverlight sandbox.

Visual Studio Integration (via ReSharper and AgUnit)

ScreenShot142

TeamCity Integration

ScreenShot137

TeamCity Configuration

ScreenShot139

Standard StatLight Console Output

ScreenShot141

Silverlight Test Project

Some interesting points about the Silverlight Unit Tests:

  • To use EnqueueCallback and EnqueueTestComplete you need to inherit from SilverlightTest, available within the Silverlight Toolkit SDK.
  • The method’s utilising these calls must be marked with the [Asynchronous] attribute.
  • You’ll often need to tailor your code to provide hooks for the unit tests.  For example adding an event after an action occurs so that TestMethod can subscribe to the event.

ViewModel

Downloads

http://statlight.codeplex.com

http://agunit.codeplex.com

http://stevenhollidge.com/blog-source-code/SilverlightUnitTestDemo.zip

Friday 23 September 2011

RabbitMQ

RabbitMQ

Here’s a simple example of how to use RabbitMQ to provide a simple pub/sub messaging architecture (aka observer/listener pattern).

Pub/Sub example

Once the RabbitMQ server is installed we are going to create a C# client to:

  • Create a queue
  • Send 10 messages to the queue
  • Read the messages back using two different techniques (5 messages each)

The code also shows how to inspect for the last message retrieved from a subscription.

The Output

ScreenShot134

Code

 

RabbitMQ Server Installation

For the server you’ll need to first install Erlang and then run the RabbitMQ installer:

http://www.erlang.org/download.html  (Windows Binary File)

http://www.rabbitmq.com/server.html  (Installer for Windows systems)

RabbitMQ Client Libraries

For the C# client libraries:

http://www.rabbitmq.com/dotnet.html

Why would you use a queue?

Queues allow messages containing work items or events to be stored in a durable manner. This enables disconnected middleware processes to be able to read from the queue, allowing for scalable processing via multiple processes reading from the queue. By contrast, sockets and other network protocols assume that direct connections always exist.

Different message types can be used on the same queue to provide a workflow scenario. Different processes can monitor the same queue for specific message types and post back to the same queue with a message type for the next stage in the workflow, which in turn gets picked up by another process and so it goes on.

For example, we might have a Web Server dealing with HTTP traffic and 2 worker processes all reading from and writing to the same queue.

  • A web server receives a request from a user to for an order and places an OrderReceived message on queue
  • Worker 1, which had subscribed to to this message type reads the message and “confirms the order” on the back end systems.  Once completed it writes OrderConfirmed message type back to the queue, which is picked up by worker 2
  • Worker 2, which had subscribed to the OrderConfirmed message type, completes its processing and writes an OrderDespatched message type back to the queue.

In reality, “Worker X” might be a bank of multiple computers and processes to allow for a high throughput of messages to be actioned.

A nice webcast can be found here by Clemens Vasters:

http://blogs.msdn.com/b/clemensv/archive/2011/03/18/why-would-anyone-ever-use-a-message-queue.aspx

Source Code

You can download the source code for this example:

http://stevenhollidge.com/blog-source-code/RabbitMqDemo.zip

Wednesday 21 September 2011

Generating Silverlight WCF Service Proxy

As a general rule it’s always best to stay away from the the Visual Studio Add Reference service proxy/client generator.

The preferred route is to use the utility tools provided, here’s an example of using the SlSvcUtil.exe tool:

cd "C:\Program Files (x86)\Microsoft SDKs\Silverlight\v5.0\Tools"
SlSvcUtil http://localhost:1558/Services/StocksService.svc /edb /ct:System.Collections.ObjectModel.ObservableCollection`1 /r:"C:\Program Files (x86)\Microsoft Silverlight\5.0.61118.0\System.Windows.dll"

ScreenShot131


Some points of interest:



  • You’ll only need to import the two generated files into your Silverlight client project.  When importing into my project I renamed the StocksService.cs to StocksServiceClient.cs
  • The command prompt doesn’t have to be the Visual Studio Command Prompt but does require elevated admin rights
  • /edb flag enables data binding (implements INotifyPropertyChanged)
  • /ct flag sets the collection type

For more info you can hit the MSDN docs:


http://msdn.microsoft.com/en-us/library/cc197958(v=vs.95).aspx

Wednesday 14 September 2011

Simple Serialization To Console

Here's a few examples of a basic data item being serialized using different techniques:

Xml, Mtom, Binary, BinarySession & Json.

ScreenShot

 

Source code: http://stevenhollidge.com/blog-source-code/SerializationExamples.zip