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=Kidnappedwould be implemented in REST as GET http://mydomain.co.uk/Bookends/books/KidnappedAnd 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=bookwould 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/KidnappedThe 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. |