Consuming RESTful Services and utilizing Basic Authentication

 

Introduction

This article assumes you have already built some RESTful Service and now curious how to write client side code in order to access and consume that service. If you appear to be yet without such service, let’s take the code download from my previous article. That service is hosted in the local IIS and was built using the ASP.NET MVC 1.0 Framework (which is not the typical approach anyone will start building RESTful Services, but see in this regard my previous posts).

Please note, this topic expects that you have downloaded and installed the WCF REST Starter Kit Preview 2.

This article will discuss these topics:

  1. Writing clients in order to consume RESTful Services using the out-of-the-box existing and available in Visual Studio 2008 networking stack (System.Net) and the System.Runtime.Serialization namespace.
  2. Writing Clients using the WCF REST Starter Kit preview 2 in order to compare this technique to the previous
  3. Addressing RESTFul Services using the WPF-Client application which is being part of the WCF REST Starter Kit preview
  4. Introducing Basic Authentication and extending the example written at step 2.

SOAP junkies remember, that utilizing SOAP based Services and creating client applications based on any service contract (represented by a WSDL) is rather a trivial task, as the WSDL and the client built upon that, will perfectly know about all the types supported by the Service. While trying to do the same in the world of RESTful Services, we are facing the unfortunate fact, that there is no such thing like WSDL and neither SvcUtil.exe nor DataSvcUtil.exe (which could be useful consuming ADO.NET Data Services) will help us to get started.

The question is, how to get some meta-data in order to start coding against such service? The general tenor is to ask the people who created or run those services. In this regard it is definitely a good idea to implement along with the RESTful Service and additional help-html page. Let say requesting http://the-service-domain/service/help we could get useful hints about the intended and supported art consuming the Service. Fortunately building RESTFul Services via the WCF REST Starter Kit Preview, such “help” response page is created automatically. That page also provides valid request examples along with their descriptions.  

A good example regarding metadata services is the twitter API description, which you can see here clicking on this link http://apiwiki.twitter.com/Twitter-API-Documentation. Another example is Microsoft’s bing API which offers boot SOAP and REST services, whereas the metadata description for REST based XML Services could be read here http://msdn.microsoft.com/en-us/library/dd250893.aspx and for REST based JSON Services here http://msdn.microsoft.com/en-us/library/dd250846.aspx.

1. Writing classic clients

Let’s assume the RESTful Service we are going to address does not have any metadata help-page yet, but rather we can capture the types the service returns. This could be done using any web browser which understands XML. If you start the Service mentioned above and discussed in the previous articles, you will get by default a similar response displayed in Picture 1. Select within the browser the Type your client is going to deal with and copy the selected structure into the clipboard (Ctrl-C).

Picture 1

Open the downloaded RestMvcApp solution and add a new Console project named e.g. ConClientApp. Add References to the System.Net and System.Runtime.Serialization assemblies, which is enough to run basic level clients while consuming RESTful Services. Open the program.cs file which is right now nearly empty and place the cursor between the the two ending curly braces after the class Program (Picture 2). Like stated at the beginning I assume you have installed the WCF REST Starter Kit Preview 2. Select next the User Type within your browser (Picture 1) if you haven’t done so yet, and put the content into the clipboard. In Visual Studio IDE choose and click the menu “Edit/Paste XML as Types” (Picture 3). Note, that this menu is only available with the WCF REST Starter Kit Preview 2.

Picture 2

Picture 3

Also note, that the client assembly’s ContractNamespace has been set to blank in order to avoid namespace conflicts while sending client-requests to the Service. The menu (Picture 3) will insert the User Type into the Console application. Here the exact steps to do:

  1. The client is going to send a POST request to create a new User. The request is sent utilizing System.Net.HttpWebRequest Class (see Code 1 lines 12-15).
  2. An instance of the DataContractSerilizer class will serialize the inserted and instantiated User type into the HttpWebRequests’s request-stream (Code 1 lines 20 – 23) 
  3. If the server responds with status code 201 (created) and the respond has the “Location” header containing the new resource’s web-link, the client will issue another request as follows (see Code 1 lines 27-29).
  4. The client will alter the client’s Status from “premium” to “silver” (Code 1 line 41)
  5. The client will utilize the contents of the previously returned resource’s Location (Code 1 line 29)
  6. The client will issue a PUT request however this time using the System.Net.WebClient class’s UploadData method (Code 1 lines 44, 58)

  1 static void Main(string[] args)
  2 {
  3     Uri uri =
  4         new Uri("http://localhost/RestMvcApp/RestService/ServiceRequest/");
  5
  6     User user = new User();
  7     user.FirstName = "Guy";
  8     user.LastName = "Henson";
  9     user.Status = "premium";
10
11     // First approach starting with HttpWebRequest
12     HttpWebRequest request =
13         HttpWebRequest.Create(uri) as HttpWebRequest;
14     request.Method = "POST";
15     request.ContentType = "text/xml";
16
17     request.AllowWriteStreamBuffering = true;
18
19     Stream reqStream = request.GetRequestStream();
20     DataContractSerializer serializer =
21         new DataContractSerializer(typeof(User));
22     serializer.WriteObject(reqStream, user);
23     reqStream.Close();
24
25     HttpWebResponse response =
26         request.GetResponse() as HttpWebResponse;
27     if (response.StatusCode == HttpStatusCode.Created)
28     {
29         string link = response.Headers["Location"].ToString();
30
31         // Loop through the headers and display them in the Console
32         Console.WriteLine("{0} : {1}",
33             response.StatusCode, response.StatusDescription);
34         for (int i = 0; i < response.Headers.Count; ++i)
35             Console.WriteLine("{0} : {1}",
36                 response.Headers.Keys[i], response.Headers[i]);
37
38         response.Close();
39
40         // Change the lastly created user’s status
41         user.Status = "silver";
42
43         // Second approch proceeding with WebClient class
44         WebClient client = new WebClient();
45         client.Headers.Add("Content-Type", "text/xml");
46         
47         // Update the user via "PUT"
48         // Get a byte-array of the data to be sent
49         MemoryStream stream = new MemoryStream();
50         StreamReader sr = new StreamReader(stream);
51         serializer.WriteObject(stream, user);
52         stream.Position = 0;
53
54         String content = sr.ReadToEnd();
55         stream.Close();
56         byte[] data = Encoding.ASCII.GetBytes(content);
57
58         byte[] resp = client.UploadData(new Uri(link), "PUT", data);
59
60         for (int i = 0; i < client.ResponseHeaders.Count; ++i)
61             Console.WriteLine("{0} : {1}",
62                 client.ResponseHeaders.Keys[i], client.ResponseHeaders[i]);
63
64         // Decode and display the response (if any)
65         Console.WriteLine("Response: {0}",
66             System.Text.Encoding.ASCII.GetString(resp));
67     }
68 }

 

Code 1

2. Writing clients via WCF REST Starter Kit

Let’s do the very same however this time building a console client based on convenience classes of the WCF REST Starter Kit Preview 2. Create another Console project within the same solution and name the project named ConAppClientKit2. Add immediately References to these assemblies:

  • Regular .NET 3.5 stuff
    • System.Runtime.Serialization
  • WCF REST Starter Kit Preview 2 stuff (located within “%installdir%\Microsoft WCF REST\WCF REST Starter Kit Preview 2\Assemblies”)
    • Microsoft.Http
    • Microsoft.Http.Extensions
    • Microsoft.ServiceModel.Web

Insert the User type in the very same manner manually as we did that in the first example utilizing the “Paste XML as Types” menu (Picture 3). Do not forget to provide [assembly: ContractNamespace("", ClrNamespace = "ConAppClientKit2")] which will reset the assembly’s namespace to blank.

Next let’s create four static methods, one for each CRUD Operation, meaning Create, Read, Update and Delete. The Main method could look like Code 2 depicts. Note the new Microsoft.Http.HttpClient Class (Code 2 line 3) which will server as center-piece in all subsequent operations. CreateNewUser will create a new user and returns the new user’s resource Id. The ReadNewUser method takes that resource Id and will read and de-serialize the previously created user. The UpdateNewUser will alter Status form premium to silver and send changes to the service, whereas DeleteNewUser delete that user base on the known resource Id.

  1 static string ServiceUri =
  2     "http://localhost/RestMvcApp/RestService/ServiceRequest/&quot;;
  3 static HttpClient client = new HttpClient();
  4
  5 static void Main(string[] args)
  6 {
  7     // This will create a brand new user
  8     string resourceId = CreateNewUser();
  9     // This will read a deserialize the lastly created user
10     User user = ReadNewUser(resourceId);
11     // This will update the user
12     UpdateNewUser(user, resourceId);
13     // This will delete the user
14     DeleteNewUser(resourceId);
15
16     Console.WriteLine("Press any key to exit");
17     Console.ReadKey();
18 }

Code 2

Note in CreateNewUser method (Code 3 line 8) the Microsoft.Http.HttpContentExtensions class which has several extension methods each of them returning Microsoft.Http.HttpContent which can be used directly while posting or putting requests via the HttpClient instance (Code 3 line 9).

  1 public static string CreateNewUser()
  2 {
  3     User user = new User();
  4     user.FirstName = "Guy";
  5     user.LastName = "Henson";
  6     user.Status = "premium";
  7
  8     HttpContent body = HttpContentExtensions.CreateXmlSerializable(user);
  9     using (HttpResponseMessage response = client.Post(ServiceUri, body))
10     {
11         response.EnsureStatusIs(HttpStatusCode.Created);
12         Console.WriteLine("Created new user: {0}", response.Headers["Location"]);
13         return response.Headers["Location"];
14     }
15 }
 

Code 3

The remaining three methods are displayed at Code 4. Note everywhere the client returns Microsoft.Http.HttpResponseMessage, which is checked via the EnsureStatusIs method. If the returned status doesn’t match the expected values, this method will throw a new System.ArgumentOutOfRangeException which is the client’s responsibility to deal with.

  1 public static User ReadNewUser(string resourceId)
  2 {
  3     using (HttpResponseMessage response = client.Get(resourceId))
  4     {
  5         // Throws an exception if not OK
  6         response.EnsureStatusIs(HttpStatusCode.OK);
  7
  8         User user = response.Content.ReadAsXmlSerializable<User>();
  9         Console.WriteLine("Read new user:\r\n{0}\r\n{1}\r\n{2}\r\n{3}",
10                 user.FirstName,
11                 user.LastName,
12                 user.Status,
13                 user.Id);
14         return user;
15     }
16   
17 }
18
19 public static void UpdateNewUser(User user, string resourceId)
20 {
21     user.Status = "silver";
22     HttpContent body = HttpContentExtensions.CreateXmlSerializable(user);
23     using (HttpResponseMessage response = client.Put(resourceId, body))
24     {
25         response.EnsureStatusIs(HttpStatusCode.Accepted, HttpStatusCode.OK);
26         Console.WriteLine("User with the Id {0} has been updated", user.Id);
27     }
28 }
29
30 public static void DeleteNewUser(string resourceId)
31 {
32     using (HttpResponseMessage response = client.Delete(resourceId))
33     {
34         response.EnsureStatusIs(HttpStatusCode.OK);
35         Console.WriteLine("User {0} has been deleted", resourceId);
36     }
37 }
 

Code 4

Comparing the two techniques above we could conclude, that the major benefit using the WCF REST Starter Kit Preview 2 provided classes while building client applications, is the convenience offered preparing (serializing) the body of the request and de-serializing the response whereas the developer is not forced to assemble these sent and received messages at the very low byte-array-level, but rather deal with strongly typed message payloads. There are of course also further benefits like the possibility to consume RESTful Services asynchronously, which however I’m not going to discuss further, just let me remind you, that this feature could be highly beneficial while integrating RESTful clients into Web-Applications.

3. The WPF Sample Client

The WCF REST starter Kit Preview 2 comes along with a WPF client application, which you could find in the “%installdir%\Microsoft WCF REST\WCF REST Starter Kit Preview 2\WCF REST Starter Kit Preview 2.zip\HttpClient\Samples\WpfSample” folder. Just extract the solution from the zipped original folder and rebuild it. You can immediately start the application which offers similar functionality as Fiddler does (Picture 4).

Picture 4

4. Basic Authentication

The last topic to discuss is adding basic authentication both to the client and to the server. The reason I have decided to show this basic level authentication example is the confusion around this IIS Authentication module. Often Basic Authentication is misinterpreted as a loosely coupled Forms Authentication, whereas it is not. The Basic Authentication module within IIS is rather a reduced subset of the Windows Authentication stack and it is hardwired against existing windows accounts. Using Basic Authentication against non Windows Accounts is rather a very tough task. Dominic Baier  wrote last year an article (Custom Basic Authentication for IIS) about this topic and also provided a self developed Basic Authentication Module example for IIS which you can download from the codeplex web site. 

So what, in order to use the built-in Basic Authentication module let’s create two local test accounts on your development computer e.g. yourcomputer\Connan being part of the Users group, and yourcomputer\Albert being part of the Power Users group (Picture 5). Let authorize Albert to do any of the CRUD operations and preventing Connan from deleting and updating accounts.

Picture 5

The client code will change slightly as Code 5 below depicts. Please note on lines 4 and 5 two new static fields keeping the network credentials for Connan (simpleCredential ) and Albert (elevatedCredential). These credentials are initialized calling InitializeCredentials() on line 9. Furthermore a new try-catch block is added in order to handle the exceptions thrown by calling the EnsureStatusIs method (Code 4) within the CRUD-Methods. This however doesn’t have anything common with authentication.

  1 static string ServiceUri =
  2     "http://localhost/RestMvcApp/RestService/ServiceRequest/&quot;;
  3 static HttpClient client = new HttpClient();
  4 static NetworkCredential simpleCredential = null;
  5 static NetworkCredential elevatedCredential = null;
  6
  7 static void Main(string[] args)
  8 {
  9     InitializeCredentials();
10
11     try
12     {
13         // This will create a brand new user
14         string resourceId = CreateNewUser();
15         // This will read a deserialize the lastly created user
16         User user = ReadNewUser(resourceId);
17         // This will update the user
18         UpdateNewUser(user, resourceId);
19         // This will delete the user
20         DeleteNewUser(resourceId);
21     }
22     catch (ArgumentOutOfRangeException exception)
23     {
24         Console.WriteLine("Process terminated: {0}", exception.Message);
25     }
26
27     Console.WriteLine("Press any key to exit");
28     Console.ReadKey();
29 }
30
31 private static void InitializeCredentials()
32 {
33     string userName1 = @"yourcomputer\Connan";
34     string userPasword1 = "The1Barbare+";
35     simpleCredential = new NetworkCredential(userName1, userPasword1);
36
37     string userName2 = @"yourcomputer\Albert";
38     string userPasword2 = "The1Brave+";
39     elevatedCredential = new NetworkCredential(userName2, userPasword2);
40 }
41

Code 5

In order to send the initialized credentials to the  RESTful Service the credentials  have to be assigned to the Credentials property of the TransportSettings of the HttpClient like the single line of code inside the DeleteNewUser method (Code 6 line 3) demonstrates.

  1 public static void DeleteNewUser(string resourceId)
  2 {
  3     client.TransportSettings.Credentials = elevatedCredential;
  4
  5     using (HttpResponseMessage response = client.Delete(resourceId))
  6     {
  7         response.EnsureStatusIs(HttpStatusCode.OK);
  8         Console.WriteLine("User {0} has been deleted", resourceId);
  9     }
10 }

Code 6

The last piece of the puzzle is the IIS Authentication adjustment of the Web Application which hosts the RESTful Service. Like Picture 6 depicts, the only allowed authentication module is the Basic Authentication. All further Authentications has to be set into the disabled state. 

Picture 6

Additionally let insert some CAS (Code Access Security) Attributes on methods handling actions within the the ASP.NET MVC RestServiceController class (Code 7 lines 4-6, 14-16, 25-27,35-37). So in order to handle GET and POST requests the caller has to be in the Users security group (this is Connan). In order to authorize the caller executing PUT and DELETE operations, the requestor’s role must be at least “Builtin\Power Users” (which is Albert).

  1 [HandleError]
  2 public class RestServiceController : Controller
  3 {
  4     [PrincipalPermission(SecurityAction.Demand,
  5         Authenticated = true,
  6         Role = "Users")]
  7     [AcceptVerbs("GET")]
  8     [ActionName("ServiceRequest")]
  9     public ActionResult GetRequest(string id)
10     {
11         return new GetServiceActionResult(id);
12     }
13
14     [PrincipalPermission(SecurityAction.Demand,
15         Authenticated = true,
16         Role = @"Builtin\Power Users")] 
17     [AcceptVerbs("PUT")]
18     [ActionName("ServiceRequest")]
19     [OperationBehavior]
20     public ActionResult PutRequest(string id)
21     {
22         return new PutServiceActionResult(id); 
23     }
24
25     [PrincipalPermission(SecurityAction.Demand,
26         Authenticated = true,
27         Role = "Users")] 
28     [AcceptVerbs("POST")]
29     [ActionName("ServiceRequest")]
30     public ActionResult PostRequest()
31     {
32         return new PostServiceActionResult();
33     }
34
35     [PrincipalPermission(SecurityAction.Demand,
36         Authenticated = true,
37         Role = @"Builtin\Power Users")] 
38     [AcceptVerbs("DELETE")]
39     [ActionName("ServiceRequest")]
40     public ActionResult DeleteRequest(string id)
41     {
42         return new DeleteServiceActionResult(id);
43     }
44 }
 

Code 7

While testing the finished application using Fiddler, here the http negotiation protocol between the Client and the RESTful Service. Basic Authentication is turned on. Note how the Service challenges the client (401 Unauthorized) to send along with the next turn valid credentials.

  1 POST /RestMvcApp/RestService/ServiceRequest/ HTTP/1.1
  2 Content-Type: application/xml
  3 Host: 127.0.0.1
  4 Content-Length: 288
  5 Expect: 100-continue
  6 Connection: Keep-Alive
HTTP 1 – Client starts a POST requests
  1 HTTP/1.1 401 Unauthorized
  2 Cache-Control: private
  3 Content-Type: text/html; charset=utf-8
  4 Server: Microsoft-IIS/7.5
  5 WWW-Authenticate: Basic realm="127.0.0.1"
  6 X-Powered-By: ASP.NET
  7 Date: Mon, 21 Dec 2009 20:21:28 GMT
  8 Content-Length: 6424 
 
HTTP 2 – Server answers with 401 unauthorized (challenge)
 
  1 POST /RestMvcApp/RestService/ServiceRequest/ HTTP/1.1
  2 Content-Type: application/xml
  3 Authorization: Basic c3JlcGFzdzdyXENvbm5hbjoxYmFyYmFyZSs=
  4 Host: 127.0.0.1
  5 Content-Length: 288
  6 Expect: 100continue

HTTP 3 – Client is going to send the Authorization header containing base64 encoded only username & password 

  1 HTTP/1.1 201 Created
  2 Cache-Control: private
  3 Location: http://127.0.0.1/RestMvcApp/RestService/ServiceRequest/3
  4 Server: Microsoft-IIS/7.5
  5 X-AspNetMvc-Version: 1.0
  6 X-AspNet-Version: 2.0.50727
  7 X-Powered-By: ASP.NET
  8 Date: Mon, 21 Dec 2009 20:21:28 GMT
  9 Content-Length: 0

HTTP 4 – Server is satisfied and answers with 201 created

The complete solution is available for download from this location:

http://cid-8d365142bc4869ab.skydrive.live.com/embedicon.aspx/.Documents/RestMvcAppV2.zip

Advertisements
This entry was posted in Uncategorized - Common. Bookmark the permalink.

One Response to Consuming RESTful Services and utilizing Basic Authentication

  1. My programmer is trying to convince me to move to .
    net from PHP. I have always disliked the idea because of the costs.
    But he’s tryiong none the less. I’ve been using WordPress on
    various websites for about a year and am nervous about switching to another platform.
    I have heard good things about blogengine.net.

    Is there a way I can transfer all my wordpress posts into it?
    Any kind of help would be greatly appreciated!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s