The solution was to combine the ideas here and here using a mixture of IHttpModule and regular expressions to provide a solution allowing any URL to be mapped in the configuration file. The code is below and is made up of RestModule.cs and some rules in the web.config.
This code will actually work for any URL you wish to make RESTful but my target was WCF.
RestModule.cs
using System;
using System.Configuration;
using System.Web;
using System.Xml;
using System.Text.RegularExpressions;
using System.Xml.Xsl;
public class RestModule : IHttpModule
{
public void Dispose()
{ }
public void Init(HttpApplication app)
{
app.BeginRequest += delegate
{
HttpContext ctx = HttpContext.Current;
string path = ctx.Request.AppRelativeCurrentExecutionFilePath;
if (ctx.Request.Url.Query.Length > 0) path = path + ctx.Request.Url.Query;
string zSubst = ReWriter.Process(path);
//did we have a new path match?
if (!zSubst.Equals(path))
{
int i = zSubst.IndexOf('/', 2);
if (i > 0)
{
string svc = null;
string rest = null;
string qs = null;
svc = zSubst.Substring(0, i);
rest = zSubst.IndexOf('?') > -1 ? zSubst.Substring(i, zSubst.Length - i).Split('?')[0] : zSubst.Substring(i, zSubst.Length - i);
qs = zSubst.IndexOf('?') > -1 ? zSubst.Split('?')[1] : null;
ctx.RewritePath(svc, rest, qs, false);
}
}
};
}
}
public class ReWriter : IConfigurationSectionHandler
{
protected XmlNode _oRules = null;
public string GetSubstitution(string zPath)
{
Regex oReg;
foreach (XmlNode oNode in _oRules.SelectNodes("rule"))
{
oReg = new Regex(oNode.SelectSingleNode("url/text()").Value, RegexOptions.IgnoreCase);
Match oMatch = oReg.Match(zPath);
if (oMatch.Success)
{
return oReg.Replace(zPath, oNode.SelectSingleNode("rewrite/text()").Value);
}
}
return zPath;
}
public static string Process(string requestpath)
{
ReWriter oRewriter = (ReWriter)System.Configuration.ConfigurationManager.GetSection("system.web/urlrewrites");
return oRewriter.GetSubstitution(requestpath);
}
#region Implementation of IConfigurationSectionHandler
public object Create(object parent, object configContext, XmlNode section)
{
_oRules = section;
// TODO: Compile all Regular Expressions
return this;
}
#endregion
}
Add a new section group to your web.config ...
<sectionGroup name="system.web">
<section name="urlrewrites" type="ReWriter"/>
</sectionGroup>
Finally add some rules in the web.config in the system.web section ...
<urlrewrites>
<rule>
<url>/MapService/GetLocation\?(.*)</url>
<rewrite>/MapService.svc/GetLocation?$1</rewrite>
</rule>
<rule>
<url>/MapService/GetLocation/(.*)</url>
<rewrite>/MapService.svc/GetLocation?target={"Name":"$1"}</rewrite>
</rule>
</urlrewrites>
Now you can have ...
http://site.com/WebSite/MapService/GetLocation?target={"Name":"Starbucks%20Manhattan"}
or ....
http://site.com/WebSite/MapService/GetLocation/Starbucks%20Manhattan
Neat heh?
2 comments:
GetLocation as a name for a resource is not very RESTful. I suggest you change that to something like Locations. Anyway, for renaming, you should check out the video posted here: http://msstudios.vo.llnwd.net/o21/mix08/08_WMVs/T01.wmv
I think GetLocation is a good name for a RESTful service as it's very much unambiguous for a start.
As a real world example, check the Flickr API - possibly the most successful on the web.
http://www.flickr.com/services/api/
Thanks for the vid pointer.
Post a Comment