How to Build a REST app in .NET (with WCF)

My prior post talked about how NOT to write a REST app in .NET. I mentioned WCF as the preferred option. In this post, I'll describe the steps for how you should do it.

Some background

First up, you should use WCF to build your REST app. WCF is the Windows Communication Foundation. ( Microsoft has such a strong penchant for creating acronyms, that there are web sites dedicated solely to cataloging them. ) WCF is part of the .NET Framework, first added in .NET 3.0. WCF is used by applications developers when building apps that communicate, whether using REST, SOAP, or something else. These days, most applications need to communicate.

The basic metaphor in WCF is that services receive and respond to incoming communication, and clients initiate those communications. The communication is modeled as an interface in code, and the service is a class or type that implements that interface. Services run within a host on any Windows computer - a desktop, laptop or a big server machine. You can also run services on Windows Mobile devices like smart phones!

Therefore for any WCF app, you need a service interface, a service implementation, and a host.

We'll be building a REST service - an application that receives and understands HTTP GET Requests according to the REST pattern.

No Visual Studio required

Contrary to what you may believe, you do not need to have Visual Studio in order to build .NET applications, including WCF applications. But all of the posts I have seen on the topic assume Visual Studio. VS2008 is the commercial application developer's tool that Microsoft sells; the 2008 version includes some nice WCF design and development tools. It is by far the easiest way to build a WCF app; using VS2008 to build WCF apps also allows you to take advantage of other Visual Studio goodies like unit testing, Intellisense, debugging, profiling, and so on. I highly recommend it. If, however, you just want to get something done quickly and you don't have and don't want to pay for the commercial tool, you can also build WCF apps using a text editor (like emacs), and the command line tools included in the .NET SDK (a free download). In this post, I will show you how.

Hosting a WCF Service

WCF provides various options when it comes to hosting a service. These options are:

  1. "Self hosted" - within a Console or Windows Forms application.
  2. Hosted within IIS
  3. As a Windows Service
  4. Hosted within Windows Activation Service. This option was added with IIS7, available in Windows Vista or Windows Server 2008.

You can read more about these options, and how to choose between them, here.

My Recommendation: Do your WCF Prototype work in a self-hosted app.

If you are developing an application to prototype some ideas, then you probably want the simplest environment possible. This, for me, is often the self-hosted route - it gives me the most control and the lightweight approach I need. Hosting WCF services within IIS can also be a lightweight approach. For this post, though, I'm going to show you how to write a WCF app that is self-hosted in a console EXE. This way you can use the console output as a diagnostic tool while tweaking and tuning the app. You can start it and stop it manually. It's easy and accessible.

There is a nifty WCF Service Host that is included as part of Visual Studio 2008, called WcfSvcHost.exe. This is a Windows Forms app that hosts any WCF service you create. It collapses into the system tray, it's got some nice usability features. On the other hand, (1) I'm presuming no Visual Studio, so you don't have WcfSvcHost.exe, dear reader. And (2), this hosting app doesn't give me the control I want. [In particular, it does not give me control over how to handle errors in the service layer, which is something I really want. ]

Rolling your own host for a WCF service is a pretty simple affair. Here's my code to do it.

1 namespace ionic.samples.WcfRest

2 {

3

4 // console-based app to Host the WCF / REST service

5 public class ConsoleServiceHost

6 {

7 public static void Main()

8 {

9 string addressRoot = "https://localhost:8080/";

10

11 // get the type we are hosting

12 System.Type t1 = typeof(ionic.samples.WcfRest.NorthwindDataService);

13

14 // Create a new ServiceHost to start and stop the service

15 // (The URI specified here is used for MEX inquiries)

16 System.ServiceModel.ServiceHost serviceHost1 =

17 new System.ServiceModel.ServiceHost(t1, new System.Uri(addressRoot));

18

19 // Create the REST binding

20 System.ServiceModel.WebHttpBinding restBinding = new System.ServiceModel.WebHttpBinding();

21

22 // Create the REST endpoint. We need to do this explicitly in order to add a custom behavior to it.

23 var restEndPoint =

24 new System.ServiceModel.Description.ServiceEndpoint

25 (System.ServiceModel.Description.ContractDescription.GetContract(typeof(ionic.samples.WcfRest.IDataService)),

26 restBinding,

27 new System.ServiceModel.EndpointAddress(addressRoot + "rest"));

28

29 // Add a customm behavior to the REST endpoint

30 restEndPoint.Behaviors.Add(new ionic.samples.WcfRest.FaultingWebHttpBehavior());

31

32 // Add the endpoint to the service

33 serviceHost1.Description.Endpoints.Add(restEndPoint);

34

35 // Open the ServiceHost to create listeners and start listening for messages

36 serviceHost1.Open();

37 System.Console.WriteLine("Service implementation: " + t1.ToString());

38 System.Console.WriteLine("The service is ready.");

39 System.Console.WriteLine("Service Address: " + addressRoot + "rest");

40 System.Console.WriteLine();

41 System.Console.WriteLine("Press <ENTER> to terminate the service.");

42 System.Console.WriteLine();

43 System.Console.ReadLine();

44

45 // Close to shutdown the service

46 serviceHost1.Close();

47 }

48 }

49 }

I will call your attention to some highlights there: First, you will see that things like base addresses, service interfaces, and service implementation classes are all specified in code (line 12, 17, 25, etc). This stuff could be specified in config files, and commonly is. But in this case, I am specifying all of it in code. There is no config file required here.

Second, is the use of WebHttpBinding, on line 20. This is the binding in WCF, new for .NET 3.5, that supports REST.

Finally, a subtle bit - notice the custom behavior I added to the endpoint. FaultingWebHttpBehavior on line 30. That behavior gives me the error handling control I mentioned earlier. More about that later.

Ok, that's the host. Now I need the service interface and the service implementation.

The Interface

The database I am using is the Northwind sample database which has been shipped with Microsoft database products for years. My service will simply return information for orders in the database - order date, customer, order contents, shipping address, and so on.

This is a simplistic interface, with just one method. It takes a single parameter as input - the order ID - and returns order information in an XML document. Here's the final product:

1 [ServiceContract(Namespace = "urn:ionic.samples.2008.03.wcfrest")]

2 public interface IDataService

3 {

4 [OperationContract]

5 [WebGet(

6 BodyStyle = WebMessageBodyStyle.Bare,

7 RequestFormat = WebMessageFormat.Xml,

8 ResponseFormat = WebMessageFormat.Xml,

9 UriTemplate = "Northwind/Order/{orderId}")]

10 OrderInfoMsg GetOrderInfo(string orderId);

11 }

Things to notice:

  1. Standard WCF-isms like OperationContract and ServiceContract.
  2. The WebGet attribute is new for .NET 3.5, and is the one you want to use for a method that handles REST requests.
  3. The UriTemplate specifies the form of the URI requests this method will be tickled for. Also it does automagic URI-to-method-parameter mapping.

The interface for a service in WCF is exhibited at multiple levels - in code (C# in this case), and on the wire (XML in this case). When doign web services design, a best practice for interoperability is to define the WSDL first, and then implement the services and clients from that WSDL. But REST has no WSDL, and the development style is more rapid iteration. In keeping with that tradition, in my case, I defined the interface in C#, and then iterated on it until I got XML in a shape I liked.

The OrderInfoMsg type, which is the response type I finally arrived at here, looks like this:

1 [DataContract(Namespace = "urn:ionic.samples.2008.03.Rest")]

2 public class OrderInfoMsg

3 {

4 [DataMember]

5 public OrderT Order;

6 [DataMember]

7 public ApplicationFaultT Fault;

8

9 [DataMember]

10 public MessageInfoT MessageInfo;

11

12

13 public OrderInfoMsg()

14 {

15 MessageInfo = new MessageInfoT();

16 }

17 }

There are 3 parts to the class - one part contains information about the order, one part to contain information about any fault that occurred on the server side, and a final part to contain generic message information. The fault information is key, because REST doesn't formally specify a way to handle faults, or what they look like. So your REST application has to consider that itself. My approach is to include a way to pass fault information back to the caller explicitly. The MessageInfo part gets filled implicitly, with a timestamp and other information. Think of that as a basic message "dog tags".

I won't show all the code for the other classes. They are just basic Data Transfer Objects.

The Service Implementation

The service implementation is obviously going to be pretty basic. All we need to do is query SQL Server for the order information, and then form an XML document in reply.

Here's where there is some fun involved. We can use LINQ-to-SQL to do the queries and fill the Data Transfer Object. Check the code:

1 public OrderInfoMsg GetOrderInfo(string orderId)

2 {

3 int n = 0;

4 try

5 {

6 n = System.Int32.Parse(orderId);

7 }

8 catch

9 {

10 return new OrderInfoMsg

11 {

12 Fault = new ApplicationFaultT

13 {

14 message = "Bad input, please pass a valid order number",

15 code = 11

16 }

17 };

18 }

19

20 OrderInfoMsg oim = null;

21 try

22 {

23 oim =

24 (from o in db.Orders

25 where o.OrderID == n

26 select new OrderInfoMsg

27 {

28 Order = new OrderT

29 {

30 OrderID = o.OrderID,

31 Customer = (from c in db.Customers

32 where o.CustomerID == c.CustomerID

33 select new CustomerT

34 {

35 CompanyName = c.CompanyName,

36 ContactName = c.ContactName,

37 ContactTitle = c.ContactTitle,

38 Phone = c.Phone

39 }).Single(),

40 OrderDate = o.OrderDate,

41 ShipInfo = new ShipInfoT

42 {

43 ShipName = o.ShipName,

44 ShipAddress = o.ShipAddress,

45 ShipCity = o.ShipCity,

46 ShipPostalCode = o.ShipPostalCode,

47 ShipCountry = o.ShipCountry,

48 },

49 Items =

50 (from od in db.Order_Details

51 where od.OrderID == o.OrderID

52 select new ItemT {

53 ProductName = od.Product.ProductName,

54 Quantity = od.Quantity,

55 UnitPrice = od.UnitPrice }).ToArray()

56 }

57 }).Single();

58

59 }

60 catch (System.Exception exc1)

61 {

62 oim = new OrderInfoMsg

63 {

64 Fault = new ApplicationFaultT

65 {

66 message = "Could not retrieve Order Information. Maybe you passed bad input?",

67 innerMessage = "Exception: " + exc1.Message,

68 code = 12

69 }

70 };

71 }

72

73 return oim;

74

75 }

Lines 4 through 18 are just validation of the input. The LINQ stuff starts on line 21. Line 26 is where I create the new OrderInfoMsg and the following lines fill that data structure based on information retrieved from the SQL database.

At first that LINQ stuff may look novel and confusing, but it is really a simple way to do database queries. Keep in mind though, that the REST stuff in WCF is not dependent on LINQ in any way. You could create and fill the OrderInfoMsg any old way, including using an old-fashioned DataReader if you like.

But I find LINQ a nice upgrade. At line 49, you can see I fill an array of line items for the order. That's nice, concise, readable code.

In line 62 you can see some error handling, and the use of the object initializers that are new for C# 3.0. Again, nice, clean code.

Where does this LINQ stuff come from?

The LINQ mappings for the Northwind database are defined in a generated C# file, which is not shown here, and which, I confess, I produced using Visual Studio. I know, I know. I promised that VS wasn't required. But I figured that digression was allowed, seeing as how the main point of this post is not LINQ but how to use WCF to build a REST app. You don't need LINQ to retrieve data. It's just a little extra BAM! for you in this sample.

The FaultingWebHttpBehavior

I found that when there was an error in the service, it would return an HTML page to the caller. This has been noticed by others. A fellow by the name of Andre posted code for an endpoint behavior that would prevent the generation of HTML messages. I liked it and used it here. This is the custom behavior I added to the endpoint, in the service host code, shown above.

1 namespace ionic.samples.WcfRest

2 {

3 /// <summary>

4 /// A <see cref="WebHttpBehavior"/> that returns error messages to the caller

5 /// when an exception occurs on the server/service.

6 /// </summary>

7

8 public class FaultingWebHttpBehavior : System.ServiceModel.Description.WebHttpBehavior

9 {

10

11

12 protected override void AddServerErrorHandlers(System.ServiceModel.Description.ServiceEndpoint endpoint,

13 System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)

14 {

15 endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();

16 endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ErrorHandler());

17 }

18

19 public class ErrorHandler : System.ServiceModel.Dispatcher.IErrorHandler

20 {

21 #region IErrorHandler Members

22

23 public bool HandleError(System.Exception error)

24 {

25 return true;

26 }

27

28 public void ProvideFault(System.Exception error,

29 System.ServiceModel.Channels.MessageVersion version,

30 ref System.ServiceModel.Channels.Message fault)

31 {

32 System.ServiceModel.FaultCode faultCode =

33 System.ServiceModel.FaultCode.CreateSenderFaultCode(error.GetType().Name,

34 "urn:ionic.samples.serviceexception");

35 fault = System.ServiceModel.Channels.Message.CreateMessage(version, faultCode, error.Message, null);

36 }

37

38 #endregion

39 }

40 }

41 }

The REST messages

The request looks something like this:

1 GET /rest/Northwind/Order/11049 HTTP/1.1

2 Accept: */*

3 Accept-Language: en-us

4 UA-CPU: x86

5 Accept-Encoding: gzip, deflate

6 User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0;)

7 Host: dinoch-2:8080

8 Proxy-Connection: Keep-Alive

Obviously, any application on any computer can generate that sort of request. I used an IE browser, but it could be anything, and in a real REST app it won't be just a browser.

And the response looks like this:

1 HTTP/1.1 200 OK

2 Content-Length: 1189

3 Content-Type: application/xml; charset=utf-8

4 Server: Microsoft-HTTPAPI/2.0

5 Date: Thu, 20 Mar 2008 20:34:59 GMT

6

7 <OrderInfoMsg ...>

The prettified XML looks like this:

1 <OrderInfoMsg xmlns="urn:ionic.samples.2008.03.Rest" xmlns:i="https://www.w3.org/2001/XMLSchema-instance">

2 <Fault i:nil="true"/>

3 <MessageInfo>

4 <ApplicationId>319a80e4-3e64-4328-a5d1-468b354c370a</ApplicationId>

5 <ApplicationName>WcfRestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</ApplicationName>

6 <OkToStore>true</OkToStore>

7 <generated>2008-03-20T13:34:59.3459363-07:00</generated>

8 <originatingHost>dinoch-2</originatingHost>

9 </MessageInfo>

10 <Order>

11 <Customer>

12 <CompanyName>Gourmet Lanchonetes</CompanyName>

13 <ContactName>André Fonseca</ContactName>

14 <ContactTitle>Sales Associate</ContactTitle>

15 <Phone>(11) 555-9482</Phone>

16 </Customer>

17 <Items>

18 <Item>

19 <ProductName>Chang</ProductName>

20 <Quantity>10</Quantity>

21 <UnitPrice>19.0000</UnitPrice>

22 </Item>

23 <Item>

24 <ProductName>Queso Manchego La Pastora</ProductName>

25 <Quantity>4</Quantity>

26 <UnitPrice>38.0000</UnitPrice>

27 </Item>

28 </Items>

29 <OrderDate>1996-05-24T00:00:00</OrderDate>

30 <OrderID>11049</OrderID>

31 <ShipInfo>

32 <ShipAddress>Av. Brasil, 442</ShipAddress>

33 <ShipCity>Campinas</ShipCity>

34 <ShipCountry>Brazil</ShipCountry>

35 <ShipName>Gourmet Lanchonetes</ShipName>

36 <ShipPostalCode>04876-786</ShipPostalCode>

37 <ShipRegion i:nil="true"/>

38 </ShipInfo>

39 </Order>

40 </OrderInfoMsg>

Clean, simple, easy.

But wait! There's more! What if you don't want XML but want that data in JSON? A simple flip of an attribute in the Service definition gives us this as a response:

1 {

2 "Fault":null,

3 "MessageInfo":{

4 "ApplicationId":"319a80e4-3e64-4328-a5d1-468b354c370a",

5 "ApplicationName":"WcfRestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",

6 "OkToStore":true,

7 "generated":"\/Date(1206047182582-0700)\/",

8 "originatingHost":"dinoch-2"

9 },

10 "Order":{

11 "Customer":{

12 "CompanyName":"La maison d'Asie",

13 "ContactName":"Annette Roulet",

14 "ContactTitle":"Sales Manager",

15 "Phone":"61.77.61.10"

16 },

17 "Items":[

18 {"ProductName":"Guaraná Fantástica","Quantity":10,"UnitPrice":4.5000}

19 ],

20 "OrderDate":"\/Date(833180400000-0700)\/",

21 "OrderID":11051,

22 "ShipInfo":{

23 "ShipAddress":"1 rue Alsace-Lorraine",

24 "ShipCity":"Toulouse",

25 "ShipCountry":"France",

26 "ShipName":"La maison d'Asie",

27 "ShipPostalCode":"31000",

28 "ShipRegion":null

29 }

30 }

31 }

Pretty nifty, eh?

That's how to write a REST app in .NET.

Where are we?

Well, I hope I've shown you a couple things here. First, it's easy to use WCF to build REST apps, using the .NET Framework 3.5. Second, you can build WCF apps with free command line tools. Finally, LINQ is cool, and is a nice complement to REST approaches.

Grab the source code attached here and try it yourself.

WcfRest.zip