Welcome to Kenn  and Scott's  Effective REST Services via .NET Page
 
0321613252.jpg (12,288 bytes)
 
Download Source Code:
Source Code Here
 
Errata:
 
Bugs. Scott and I hate them, but we also realize they're a fact of life. If you find a technical error in this book, we'd very much like to know about it so that we can make corrections in future editions and reprints. Thank you for reading, and thank you for sending in comments, both good and bad. We read every one!
 
Make a comment.
 
Here are the known issues:
 
  1. Updated .NET Services CTP SDK Released
  2. DELETE and Idempotence
  3. Kenn on .NET Rocks!
  4. More thoughts on Azure
  5. Updated Book Code-Chapter 7 Security Test Data Bug
  6. IIS, WAS, and worker requests
  7. Create, POST, PUT, and response
  8. 405 Error using MVC Sample and IIS 7.5
  9. Unsupressed IIS7 Error Messages
  10. Changes to Support Windows 7
  11. X-HTTP-Method-Override
  12. Asynchronous Authentication Module Implementation
  13. Updated MVC web.config file
 
Updated .NET Services CTP SDK Released

When Chapter 9 was originally written, we were using the November 2008 Community Technical Preview (CTP) of the .NET Services Software Development Kit (SDK). From the time the chapter was submitted to the time the book went to the printers, Microsoft released several new SDK versions, with the latest (March 2009) available here.

The code available at the publisher's Web site will remain at the March 2009 CTP version, but updated code is available from the link above as newer SDK versions are released. (As of this time, the publisher's book source code and the source code available here are identical.)

 
DELETE and Idempotence

A reader writes (anonymously):

"I don't think your interpretation of HTTP DELETE in terms of idempotence is correct. On the first DELETE, a resource is destroyed, true, and subsequent DELETE requests to the same URI should not effect the server state; i.e., nothing else should be deleted. But the DELETE is not safe, and sending back a 200 OK is not the only option as you seem to defend. In fact it is much more intuitive to send 410 GONE to indicate that the resource used to exist, but is now deleted. This does not break indempotence, because the server state has not actually changed since your first call, what's gone is still gone."

(From Kenn.) DELETE is by definition idempotent, but not safe, and I don't believe I claimed it to be safe. :) If you read sections 9.1.1 and 9.1.2 of RFC 2616 I think you'll find my interpretation correct. On page 43, I say "Therefore, GET and HEAD are defined to be safe..." and on page 44 I say "In the final analysis, GET, HEAD, PUT, and DELETE are defined to be idempotent." The remaining paragraphs in the DELETE discussion on page 45 are relaying what RFC 2616 tells us in its section 9.7, "The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location."

Regarding returning a HTTP response 410 (Gone), again you should consult RFC 2616, where it states in section 9.7, "A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity." This is the guidance I followed when I wrote Chapter 2.

You are not, therefore, generally free to return a 410 response code for a DELETE. 410 instead, by definition in section 10.4.11 of RFC 2616, indicates "The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent." The implication, also stated in section 10.4.11, is this: "The 410 response is primarily intended to assist the task of web maintenance by notifying the recipient that the resource is intentionally unavailable and that the server owners desire that remote links to that resource be removed. Such an event is common for limited-time, promotional services and for resources belonging to individuals no longer working at the server’s site." (Emphasis added.) Clearly this isn't involved in normal day-to-day (typical) resource management, but I grant that if your intentions follow RFC 2616's section 10.4.11, subsequent GET requests could possibly return 410. By far the normal course of action, by specification, is to delete the resource and return 200 (OK) or 204 (No Content), or even 202 (Accepted) for the first and subsequent DELETE requests.

I get the sense your interpretation of the 410 response code is based solely on the single-word description ("gone"). I say that because I don't see that returning 410 is in any way more intuitive given the true definition of the response code as stated in RFC 2616. Speaking personally, I try to not do interpret the return codes by their single-word definitions. Instead, I believe it's far better to refer to RFC 2616 and determine the true nature of the meaning of the return code and return only those codes that fit the precise definition for the given situation. To do anything else could lead (often does lead) to misuse of the HTTP protocol and client misunderstanding.

(From Scott.) As far as I know, this (returning 410) only makes sense when the item is deleted but tracking information still exists. I prefer to make deletes non-recoverable to remove any onus of knowing what used to be true. In any case, I think that 200 OK is always OK, whereas 410 is only occasionally correct and those cases are few and far between.

 
Kenn on .NET Rocks!

Kenn had the opportunity to talk with Carl and Richard on .NET Rocks!, the .NET Internet radio talk show. It was a lot of fun, actually...none of the show was pre-planned or rehersed--all of the Q+A is straight from the hip.

If you're curious to hear Kenn speak about RESTful concept, REST in conjunction with AJAX, Azure, and ASP.NET MVC, you can download one of a multitude of audio formats here.

 
More thoughts on Azure

If you read Chapter 9 in the book you'll come away with a sense that Azure allows you to create distributed systems that before were very hard to create, if not impossible. And I think for the most part this focus is a good one... But this capability stems from the .NET Service Bus, which is but one constituent Azure technology. And as it happens, over time (since Chapter 9 was written and the time I posted this note), Azure continues to grow and evolve. Moreover, Azure is becoming more RESTful, offering ways for your applications to do even more with the cloud.

For example, you can now use Azure's "simple data services" to store blobs, tables, or queues (think: message queue-type of storage) using RESTful access and interaction. You'll also be able to access Azure's computational capabilities in RESTful ways.

Unfortunately, these capabilities were not available when Chapter 9 was written, so you won't find examples of them in the book. However, if we're offered a chance to update the book for a second edition, we'll try to work some of these newer capabilities into the chapter. Meanwhile, be sure to check out Azure's Web site for more information (see http://www.microsoft.com/azure/windowsazure.mspx for more information).

 
Updated Book Code-Chapter 7 Security Test Data Bug

(Sigh) Somehow when I was creating the SQL script to load the test data into the database for Chapter 7, I copied the wrong user Guids when assigning users to roles. When you run the script SecurityScripts.sql, you'll get a foreign key error when trying to write the test data into the aspnet_UsersInRoles table. I can't tell you how that happened, with so many people looking at the code, but the fault is mine and I apologize for any inconvenience. Many thanks to reader Brad for noticing there was a security error somewhere in the mix and forcing me to look into it.

I've updated the security script, which you can download here. The entire source tree has also been updated here.

The effect is you can't log into the CodeXRC service using any of the provided users. The users exist, but since no roles are assigned, they each fail when we try to match their authorization levels. The good news is the security module did its job, but the sad news is the test data was incorrect. Run the updated script and it should work for you. If you've already run the script, simply run the last five script elements to load the aspnet_UsersInRoles table appropriately.

 
IIS, WAS, and worker requests

A reader writes (thanks Milan!):

I have a question about something I read in Chapter 5. A paragraph that precedes Figure 5.1 says, "WAS passes the request on to the IIS worker process components."

However, the IIS7 Resource Kit by Mike Volodarsky, et. al. (see this), says that WAS does quite a bit of work to ensure the right worker process is running, then passes configuration info to W3SVC, which in its turn configures and updates HTTP.sys. Eventually, "HTTP.sys forwards the request to the worker process." (You can find this duscission in Chapter 2, following Figure 2.2).

Which book is right? I must be missing something.

Scott writes: I just re-read this section to see where I goofed. Mike Volodarsky, being the man when it comes to IIS 7, is definitely right. In the IIS7 Resource Kit, Mike and his co-authors are trying to be very particular about every action that happens when a request arrives and how the response goes back to the caller. My discussion was meant to be a cursory overview of the process of getting a request to the processor, ignoring the response. As such, it contains some simplifications so that people understand that these things do some work in cooperation—and then I stopped. I apologize if I drew the line between the abstraction I keep in my head and reality in the wrong place. I tend to get mired in the details and wanted to force myself to stay at a higher level. I was assuming the readers didn’t want to know the whole choreography between WAS and a protocol provider.

I believe you are referring to content that very closely resembles http://learn.iis.net/page.aspx/101/introduction-to-iis7-architecture/ near figure 1 (near the end of the article). That figure shows the complete process of also responding to the request, whereas I stop at the point where the message gets to the app.

 
Create, POST, PUT, and response

A reader asks (thanks Marti!):

Thanks for your great book. It's a great read and very instructive. I have to admit that I'm a bit confused about two aspects though: (note from Kenn...I edited it to be three, as he asked about code separately)

  1. Is it correct to state that for CREATE operations the POST method is more appropriate than PUT since the URI of the new item is not known beforehand? And thereby that UPDATE operations should be performed by using PUT?
  2. I understand that for every action a HTTP ResponseCode should be sent (e.g. 201 when a new resource is created). However, in the sample code I only see ResponseCodes being returned when an operation failed, not when an item is created or a collection is returned. Besides this, I also don't see a Location header with the new URI added in e.g. the AddImage method in chapter 8.
  3. Is a statement like the following best practice in a WCF service?

    WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created;
    WebOperationContext.Current.OutgoingResponse.AddHeader("Location", locationValue);

Kenn writes: Well, if there were *only* two, then I'm delighted. :)

  1. Both POST and PUT are used to create. The difference essentially revolves around a) is the client mandating the ultimate URI, and b) is the new item subordinate to another item (like a blog comment, database record, XML node, or whatever). If as the client you provide the server with a representation and tell it what URI to use, you'd use a PUT. If you cannot beforehand know what the URI will be, as with some sort of item number, record ID, or whatever, then you use a POST. POST isn't patently used to create new items for every case... It is confusing at times, I agree, but it's what RFC 2616 tells us... It's what Dr. Fielding also specifies.
  2. This is only for Chapter 8? I know both Scott and I tried to look specifically for that. Having written a bunch of these things I can honestly say it's way, way too easy to overlook. But if we missed it, we'll modify the code and add it, I'm sure. The bottom line is RFC 2616 does specify when Location headers are to be used, but sometimes it's not necessarily incorrect to omit them:

    "If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header (see section 14.30)." (pp 36 in RFC 2616)

    The emphasis is on SHOULD. However, it's the right thing to do, is more correct, is more helpful, and we'll probably go back and add it if we omitted it... Sorry for any confusion there.
  3. The code seems fine to me as long as the value contained in locationValue is, in fact, the URI of the added item's representation.
 
405 Error using MVC Sample and IIS 7.5

Scott had an interesting error when running Chapter 7's MVC sample. It seems when he loaded it on his new Windows Server 2008 R2 box, which runs IIS 7.5, the MVC sample continued to return 405 errors when he tried to insert items using the service.

After using failed request tracing, he discovered that WebDAV was the culprit. WebDAV was denying the PUT requests, so Scott disabled WebDAV for the site. The sample service then worked fine.

 
Unsupressed IIS7 Error Messages

A reader asks (thanks Robert!):

In IIS7, apart from enabling "Detailed Errors" in "Error Pages" section, how can I convince the web server to transmit to client the body of an http reply with status code != 200?

For example, I want to return 400 Bad Request http error code together with an explanation message to the client. IIS7 will suppress the body in case that it is configured to return "Custom error pages" (the safest setting). If I enable "Detailed errors", IIS7 will expose my http error message but it will also divulge precious sensitive information about the inner bowels of my webserver/app in case of other errors (e.g. http status 403.4)

My response to Robert was that IIS7 has the ability to configure error reports to the client (see this).

However, he was looking for more and found a great resource that provided the secret (look here. He wanted IIS7 to leave the error entirely alone, and to do that you must use the following command from the command line:

%windir%\system32\inetsrv\appcmd.exe set config "Default Web Site/" -section:system.webServer/httpErrors -existingResponse:PassThrough

The (not recommended) command to do this for the entire IIS7 server is:

%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpErrors -existingResponse:PassThrough

 
Changes to Support Windows 7

A reader provided this information (thanks Paul!):

When you try to execute http://localhost/AspNetMvcRestService/CodeXRC/Project1, you get a number of problems on Win7 RC with IIS7, and some additional things must be done.

  1. The first problem on IIS7-Win7RC is that the CodeXRC database does not have the IIS APPPOOL\DefaultAppPool user configured for the database, so we get this:

    Server Error in '/AspNetMvcRestService' Application.
    Cannot open database "CodeXRCDatabase" requested by the login. The login failed.
    Login failed for user 'IIS APPPOOL\DefaultAppPool'.
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information
    about the error and where it originated in the code.

    Exception Details: System.Data.SqlClient.SqlException: Cannot open database "CodeXRCDatabase" requested by the login. The login failed.

    Login failed for user 'IIS APPPOOL\DefaultAppPool'.
    Source Error:

    Line 197:
    Line 198: // Authenticate
    Line 199: if (credentials.Length == 2 &&
    Line 200: Membership.ValidateUser(credentials[0], credentials[1]))
    Line 201: {

    The fix for this problem is to open Microsoft SQL Server Management Studio Express, expand the Databases node, expand the CodeXRCDatabase node, and expand the Security node. Right click on Users and add the IIS APPPOOL\DefaultAppPool as a user of the database. This user must be given the following Database role membership:

    aspnet_Membership_FullAccess
    aspnet_Personalization_FullAccess
    aspnet_Profile_FullAccess
    aspnet_Roles_FullAccess
    aspnet_WebEvent_FullAccess
    db_owner
  2. The next problem is that EXECUTE permissions are denied on the aspnet_CheckSchemaVersion object, so we get this:

    Server Error in '/AspNetMvcRestService' Application.
    The EXECUTE permission was denied on the object 'aspnet_CheckSchemaVersion', database 'CodeXRCDatabase', schema 'dbo'.
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information
    about the error and where it originated in the code.

    Exception Details: System.Data.SqlClient.SqlException: The EXECUTE permission was denied on the object 'aspnet_CheckSchemaVersion', database 'CodeXRCDatabase', schema 'dbo'.

    Source Error:

    Line 197:
    Line 198: // Authenticate
    Line 199: if (credentials.Length == 2 &&
    Line 200: Membership.ValidateUser(credentials[0], credentials[1]))
    Line 201: {

    This error will also show up on three other objects. So, you must execute this Query on CodeXRCDatabase:

    grant execute on dbo.aspnet_CheckSchemaVersion to "IIS APPPOOL\DefaultAppPool"
    grant execute on dbo.aspnet_Membership_GetPasswordWithFormat to "IIS APPPOOL\DefaultAppPool"
    grant execute on dbo.aspnet_UsersInRoles_GetRolesForUser to "IIS APPPOOL\DefaultAppPool"
    grant execute on dbo.aspnet_Membership_GetUserByName to "IIS APPPOOL\DefaultAppPool"

While I've downloaded the RTM version of Windows 7, I've not installed it (I did try the beta and loved it). When I do, I'll verify this information, but I have reason to believe Paul is correct. A huge thanks to you, Paul, for digging into this!

 
X-HTTP-Method-Override

While not normally an issue with thick clients, accessing full RESTful capabilities of available services via browsers often is problematic as many (if not all) browsers only allow a form to GET or POST. They don't allow for other HTTP methods, like HEAD, PUT, or DELETE. Google realized this and offers a solution, which is to add a header to the HTTP request, X-HTTP-Method-Override, that is supposed to be interpreted by the service and acted upon regardless of the actual HTTP method used.

The header syntax is as follows:

X-HTTP-Method-Override: PUT

You can learn more about it from http://code.google.com/apis/gdata/docs/2.0/basics.html#UpdatingEntry.

As it happens, ASP.NET MVC, version 2, supports this out of the box with the HttpHelper.HttpMethodOverride method. Just use that helper method in your view, and assuming the service honors the header, your Web page should be able to work with the RESTful service. I thought this blog entry was particularly interesting (he has an interest in using ASP.NET MVC for RESTful services as well).

 
Asynchronous Authentication Module Implementation

In the book (page 234), I promised to someday write an asynchronous version of my authentication/authorization module (used in Chapters 6-8). Well, that day has finally arrived, and you can download the refactored AuthModule.cs file here. My thanks to reader Carl for reminding me of my promise. :)

The revised source file contains both the synchronous and asynchronous versions, and it completely replaces the existing source file. To choose which one you want, simply modify web.config:

Synchronous: <add name="BasicAuthModule" preCondition="managedHandler" type="AspNetRestService.AuthModule, AspNetRestService" />

Asynchronous: <add name="BasicAuthModule" preCondition="managedHandler" type="AspNetRestService.AsyncAuthModule, AspNetRestService" />

I've tested it both synchronously and asynchronously, and it seems to work fine. However, if anyone finds bugs or issues with the code, please do drop us a line here. I also discuss the code a bit in my blog if you're interested in some discussion.

 
Updated MVC web.config file

I'm not sure I fully understand why this happened, but it happened. Microsoft, for a short period of time, released version 3.6.0.0 of System.Web.Extensions. Later, they recanted that version, and 3.5.0.0 is now the version to use (Visual Studio 2008 SP1 did this to us, I believe).

Aside from the obvious "what the heck is up with this" questions I'm sure we all have, the effect of this is the MVC project from Chapter 7 won't run after installing Visual Studio 2008 SP1. So I've updated the web.config file to use the correct version, and it now runs again. I placed this in the updated source tree (available here). I also added the updates for the asynchronous module to the code for Chapters 6-8 and updated their configuration files to use the asynchronous module for authentication. There shouldn't be any issues, but if you do happen across one, please let us know here.