RESTful Services - Exposing resources by URL in C#

1. In a RESTful archtecture, resources are accessed by unique URL, rather than by a named script plus querystring arguments, or a webservice SOAP equivalent, e.g.
   GET http://mydomain.co.uk/GetItemById.aspx?store=Bookends&category=book&title=Kidnapped
would be implemented in REST as
   GET http://mydomain.co.uk/Bookends/books/Kidnapped
And in the same way, multiple repeating items are just accessed as if they were a 'folder item' on the same resource, e.g.
   GET http://mydomain.co.uk/GetAllItems.aspx?store=Bookends&category=book
would be implemented in REST as
   GET http://mydomain.co.uk/Bookends/books/
2. To implement this GET verb as a dotNet application, it is neccessary to intercept and rewrite the url to one or more worker scripts, which actually implement the search and generation of the results. In the simple case of GET, there is no data being posted into the resource, so we can simply redirect the URL and pass in the original path as 'data' to the script, so it can decide which result set to show depending on which url was requested. To do this, you must edit the Global.Asax file, and amend the Application_BeginRequest event:
   protected void Application_BeginRequest(object sender, EventArgs e)
   {
       if (this.Context.Request.RequestType == "GET")
       {
           this.Context.RewritePath("/default.aspx?path=" + System.Web.HttpUtility.UrlEncode(Request.Url.LocalPath));
       }
   }
3. The script you write to implement the search must interrogate the path it's been passed in to determine if it's a specific search or a multiple item search. This will depend entirely on what you consider the URL structure to be, and will vary between projects. To implement the single and multiple searches as given in the examples above, the script might count the number of chunks in the path, e.g.
   string[] paths;
   System.Text.StringBuilder Output;

   protected void Page_Load(object sender, EventArgs e)
   {
       // set up the output buffer, this will be written to the output stream
       Output = new System.Text.StringBuilder();

       // get the path as passed in from Global.asax
       string LocalPath = Request["path"].ToString();

       // the path will have a leading slash, and possibly a trailing slash - so remove if there
       LocalPath.TrimStart('/').TrimEnd('/');
       LocalPath.TrimStart('\\').TrimEnd('\\');

       //split up the path into chunks
       char[] pathseparators = { '\\', '/' };
       paths = LocalPath.Split(pathseparators);

       // if path has three chunks, e.g. Bookends/books/Kidnapped then it's specific query
       if (paths.length==3)
       {
          //do specific search on exact book
          SpecificSearch(paths[0],paths[1],paths[2]);
       }

       // if path has two chunks, e.g. Bookends/books then it's multiple results query
       if (paths.length==2)
       {
          //do multiple search on all books
          MultipleSearch(paths[0],paths[1]);
       }

       // flush to output
       Response.Write(Output.ToString());
       Response.Flush();
       Response.End();
   }
4. Now implement the searches as required, querying the database and returning the results as an XML string (or any format your clients expect)
   protected void SpecificSearch(string StoreName, string Category, string BookTitle)
   {
       // set up the SQL search with the params
       SqlCommand search = new SqlCommand("SELECT * FROM ITEMTABLE WHERE STORE=@p1 AND CATEGORY=@p2 AND TITLE=@p3")
       search.Parameters.AddWithValue("@p1",StoreName);
       search.Parameters.AddWithValue("@p2",Category);
       search.Parameters.AddWithValue("@p3",BookTitle);

       // get the results set from a DAL class (somewhere) in DataTable Format
       // and append them to the output stream 
       DataTable Results = DAL.RunQuery(search);
       System.IO.StringWriter sw = new System.IO.StringWriter();
       Results.WriteXml(sw);
       Output.Append(sw.ToString());
   }

5. Now when the resource URL is invoked (in a Browser) or from a remote program calling it as a service by issuing the GET command:
   GET http://mydomain.co.uk/Bookends/books/Kidnapped
The URL is intercepted, chopped up and the script detects that this is a specific book search and executes the query, and returns the XML to the browser (or calling program) directly:
  <Book>
     <Id>344225</Id>
     <Title>Kidnapped</Title>
     <Author>Robert Louis Stevenson</Author>
     <Year>1886</Year>
     <Seller>Bookends</Seller>
     <Condition>Used - Good</Condition>
     <Price>�9.99</Price>
  </Book>
And the same process would apply for the Multiple search, only it would contain multiple Book XML fragments contained within a containing node.
6. If our remote reousrces need to POST into the app, then the process would be similar, with a small complication. Since POST data already contains data needing to be posted into the script, we would need to capture it and append this to our redirect in Global.asax:
   if (this.Context.Request.RequestType == "POST")
   {
       string PostedData = "";
       string LocalPath = System.Web.HttpUtility.UrlEncode(Request.Url.LocalPath);
       using (var reader = new System.IO.StreamReader(this.Context.Request.InputStream))
       {
            PostedData = System.Web.HttpUtility.UrlEncode(reader.ReadToEnd());
       }
       this.Context.RewritePath("/default.aspx?path=" + LocalPath + "&postdata=" + PostedData);
   }
This adds the complication at the processing end that it must parse and deconstruct the original postdata to find out the variables within, but this is fairly trivial with .Split and .UrlDecode functions. The appropriate SQL inserts and updates can be used to use the raw data (in XML or whatever format is agreed with clients) to update the database tables.
And that is all there is to RESTful webservices. Resources can be exposed as URLs and GET and POST verbs implemented. PUT and DELETE are not normally mapped by IIS to ASP.NET but can be mapped manually in the IIS Manager. These can then be captured in a similar way and mapped.