Wednesday, May 16, 2007

As we were working through the design of the UriTemplate API, we had three broad classes of scenarios in mind. One class involves creating URI's that conform to a common pattern, which our implementation supports via the BindByName and BindByPosition methods. You use these methods when you need to create the URI for a new request or build a URI that someone else will later dereference via an embedded link. I blogged some sample code that shows how this works in a previous post.

A second, more interesting class of scenarios involves determining whether a specific URI matches a given pattern. If the URI matches the pattern, we can treat the URI as structured data and parse it according to the pattern which describes its structure. This functionality is implemented by the Match and MatchSingle API's on System.UriTemplate.

Addressing the second class of scenarios (which involve a single URI and a single pattern at a time) opens the door to the third class of scenarios, which involves matching a candidate URI against a set of templates and taking some arbitrary action based on which subset of the templates matched the candidate URI. This is implemented in the System.UriTemplateTable class which forms the basis of our URI dispatch engine, the discussion of which will probably get punted to a later post.

Many aspects of the final design were influenced by the second and third classes -- as much if not more so than the simple case of Bind().

A bit about template syntax

When you write code like the following:

UriTemplate t = new UriTemplate( "literal/{var}?k1={v1}" );

you are defining a pattern expression over the path and query components of the URI. The path component is divided into segments, where each segment in the template can be one of three tokens:

  • A literal segment. Just what you'd think -- the corresponding segment in the URI must match this literal string. The match is done case-insensitively against the URL-escaped template string (canonicalization and escaping for template strings follow the logic of System.Uri).
  • A named variable segment -- the name of the segment is surrounded with curly braces. Variables can match whole segments only, and the individual names of the variables within the template must be unique. During Match(), we use the variable name to create a key/value pair where the key is the name of the variable and the value is the unescaped value of the corresponding segment in the URI being matched.
  • Path wildcard segments. We allow a special token (written as *) to appear at the end of the template as a stand in for 'the rest of the path'

The query component is written as an unordered sequence of name/value pairs. The left-hand side (the key) must be a literal value and cannot be templatized. The right-hand side can be a literal value or a named variable in {curlyBraces}.

Bind and Match are inverses

One basic operating assumption you can make about UriTemplate is that the Match() operation is the logical inverse of Bind(). If you create a URI from a template and a lexical environment using BindXXX(), you can assume that calling Match on the same template with the URI you just created will result in a successful match and create the same lexical environment you started with:

Uri baseAddress = new Uri( "http://localhost:81" );
string artist = "Led Zeppelin";
string album = "Four";
 
UriTemplate template = 
 new UriTemplate("music/{artist}/{album}?format={format}" );

Uri boundUri = template.BindByPosition( baseAddress, artist, album, "rss" );
 
//boundUri:
// http://localhost:81/music/Led%20Zeppelin/Four?format=rss

UriTemplateMatch match = template.Match( baseAddress, boundUri );

Debug.Assert( match != null );
Debug.Assert( match.BoundVariables["artist"] == artist );
Debug.Assert( match.BoundVariables["album"] == album );
Debug.Assert( match.BoundVariables["format"] == "rss" );

Although it's technically a weak invariant[1], it's a handy rule of thumb for reasoning about the intent of the design.

UriTemplateMatch

The return value of a successful match is an instance of System.UriTemplateMatch (a failed match will return null). Inside of UriTemplateMatch are a number of useful constructs:

    public class UriTemplateMatch
    {
        public UriTemplateMatch (){}
   
        //The starting point for the template match
        public Uri BaseUri{ get; }

        //The full candidate URI (as it came in off the wire -> untouched)
        public Uri RequestUri{ get; } 

        //the set of path segments, starting after EndpointUri -> decoded
        public Colletion<string> RelativePathSegments { get; }

        //a name/value view of the entire query string -> VALUES are decoded
        public NameValueCollection QueryParameters { get; }

        //a name/value view of the template variables -> decoded
        public NameValueCollection BoundVariables { get; }

        //the template that was matched -> whatever you passed into the ctor of UriTemplate
        public UriTemplate MatchedTemplate { get; }

        //the unmatched part of the path
        //(for wildcard templates, e.g. {foo}/bar/*) -> untouched
        public Collection<string> WildcardSegments{ get; }
    }

The member most relevant to this discussion is BoundVariables, which contains a name/value view of values obtained by extruding the candidate URI through the template and associating each variable in the template with its corresponding URI segment value. WildcardSegments and RelativePathSegments are quite nice because they give you just the parts of the URI that are relevant to your app (saves you from having to do unnatural acts -- like grepping through the URI to find a file extension, which is basically what you have to do on the platform today...).

I think that's probably enough rambling for one post -- I'll come back to Match() when I talk about UriTemplateTable in the next post.

Technorati Tags:

--------

[1] The only time this invariant breaks is if the values you pass to BindXXX() contain slashes. Escaped or unescaped, slashes are considered evil by System.Uri for security reasons and are never escaped on the wire. As such, a call to Bind() with values containing slashes will result in an output URI with more segments that the template it came from, which will cause the subsequent Match() to fail. This gives rise to my URI canonicalization haiku:

oy, freaking slashes
real or escaped, they all suck.
I blame Roy Fielding...

Just kidding, Roy. :)

Wednesday, May 16, 2007 12:34:04 AM (Pacific Standard Time, UTC-08:00)  #    Comments [5]Tracked by:
"Programming the Web With WCF" (TSHAK) [Trackback]
http://tshak.net/cs/blogs/tshak/archive/2007/05/29/52.aspx [Pingback]
http://9lo-free-porn.info/00108635/south-asian-women-best-hair-color.html [Pingback]
http://9ln-free-porn.info/42846412/av-sexy.html [Pingback]
http://9ls-free-porn.info/12722968/index.html [Pingback]
http://9lo-free-porn.info/70702214/video-conferencing-free-skype.html [Pingback]
http://9lo-free-porn.info/00108635/index.html [Pingback]
http://9ll-free-porn.info/19219823/index.html [Pingback]
http://9lo-free-porn.info/54234734/index.html [Pingback]
http://9lt-free-porn.info/56022390/hometics-mini-massager.html [Pingback]
http://9ln-free-porn.info/49424663/capiche-gapiche.html [Pingback]
http://9lk-free-porn.info/60589456/index.html [Pingback]
http://9ll-free-porn.info/54766723/index.html [Pingback]
http://9nk-information.info/15620719/lexus-dvd-navigation-system-2007.html [Pingback]
http://9nv-information.info/51900210/index.html [Pingback]
http://9na-information.info/25114549/flowers-callolillies.html [Pingback]
http://9nd-information.info/12850415/water-balloon-games-for-children.html [Pingback]
http://9mj-free-porn.info/08053345/index.html [Pingback]
http://9nt-information.info/93810413/index.html [Pingback]
http://9na-information.info/09573528/index.html [Pingback]
http://9mg-free-porn.info/41465530/index.html [Pingback]
http://9mf-free-porn.info/29199378/index.html [Pingback]
http://9oi-information.info/18215342/index.html [Pingback]
http://9qo-information.info/53684576/il-baule-d-oriente.html [Pingback]
http://9qa-information.info/63732179/index.html [Pingback]
http://9qc-information.info/42722391/atisreal.html [Pingback]
http://9qs-information.info/14974559/index.html [Pingback]
http://9ps-free-porn.info/39371855/index.html [Pingback]
http://9pn-free-porn.info/28128026/index.html [Pingback]
http://9sc-information.info/64662339/index.html [Pingback]
http://9ro-information.info/91492135/index.html [Pingback]
http://9sc-information.info/32058695/sito-internet-ottimizzazione.html [Pingback]
http://9ri-information.info/87703585/my-life-is-marijuana.html [Pingback]
http://9ri-information.info/41422350/index.html [Pingback]
http://9uabf-free-porn.info/50408076/movies-cartersville-georgia.html [Pingback]
http://9uaap-free-porn.info/66243251/index.html [Pingback]
http://9uaan-free-porn.info/26992656/index.html [Pingback]
http://9uaeo-le-informazioni.info/83509810/index.html [Pingback]
http://9uafc-le-informazioni.info/18438387/film-uccello.html [Pingback]
http://9uaea-le-informazioni.info/64439803/index.html [Pingback]
http://9uaef-le-informazioni.info/24128209/nasser-el-sonbaty.html [Pingback]
http://9uaer-le-informazioni.info/44919200/locazione-commerciale.html [Pingback]
http://9uafa-le-informazioni.info/02126910/index.html [Pingback]
http://9uaht-le-informazioni.info/55478665/index.html [Pingback]
http://9uaho-le-informazioni.info/97920757/index.html [Pingback]
http://9uahg-le-informazioni.info/89414545/index.html [Pingback]
http://9uaha-le-informazioni.info/38372901/villaggio-riccione.html [Pingback]
http://9uahi-le-informazioni.info/79077594/index.html [Pingback]
http://9uaga-le-informazioni.info/43346194/index.html [Pingback]
http://9uajt-free-porn.info/56046138/tab-hunter-gay.html [Pingback]
http://9uail-free-porn.info/38458477/index.html [Pingback]
http://9uald-free-porn.info/07090903/celebracion-d-ela-palabra.html [Pingback]
http://freewebs.com/toltom/05/seventh-avenue.html [Pingback]
http://freewebs.com/toltom/02/sitemap13.html [Pingback]
http://freewebs.com/toltom/16/unlaimed-money.html [Pingback]
Wednesday, May 16, 2007 5:56:41 AM (Pacific Standard Time, UTC-08:00)
Wouldn't it be more coherent with the rest of the .NET framework (e.g. System.Text.RegularExpressions) if you called the methods Match() (for single matches) and Matches() (for multiple matches) instead of MatchSingle() and Match()?
Asbjørn Ulsberg
Wednesday, May 30, 2007 4:38:02 PM (Pacific Standard Time, UTC-08:00)
Is there a way to use the UriTemplateMatch (and by extension UriTemplateTable) with just the relative path rather than the full absolute Uri? (e.g. using just the part of the Uri that corresponds to the definition of System.Web.HttpRequest.Path)?

I played around with various combinations but could only get it to work by using a full absolute URI including host.

I was hoping to do something like the following so I don't have to worry about what host I'm deployed on. I was playing around this outside the context of WCF.

Uri prefix = new Uri("/", UriKind.Relative);
UriTemplate template = new UriTemplate("service/{data}");
Uri requestUri = new Uri("/service/apple", UriKind.Relative);
UriTemplateMatch match = template.Match(prefix, requestUri);

Similarly, it would be cool if we could do something like this:

Uri prefix = new Uri("/", UriKind.Relative);
UriTemplate template2 = new UriTemplate("music/{artist}/{album}?format={format}");
Uri boundUri = template2.BindByPosition(prefix, "fred", "brown", "rss");

But apart from that, like it!
Martin Bayly
Thursday, May 31, 2007 8:43:37 PM (Pacific Standard Time, UTC-08:00)
Thanks for stopping by Martin -- glad you like the feature.

It's true that UriTemplate requires absolute URI's, but we need that more for consistent canonicalization/escaping behavior than for hostname. In fact, we don't consider hostname at all during Match(). Does that work for you?
Thursday, June 21, 2007 3:41:51 PM (Pacific Standard Time, UTC-08:00)
For those that wonder about an implementation of UriTemplate and UriPattern that you can use right now, I've written a simple open-source implementation for .Net (1.1 or 2.0) that can read about here http://musingmarc.blogspot.com/2007/06/uritemplate-project-on-codeplex.html
Thursday, June 28, 2007 6:31:56 PM (Pacific Standard Time, UTC-08:00)
Are there plans to enhance URITemplate to support Web3s and/or Astoria style Urls? I blogged about it here http://www.bizcoder.com/2007/06/29/systemuritemplate/
Comments are closed.