Raymond Camden's Blog Rss

Model-Glue Short URLs on the Cheap

19

Posted in ColdFusion | Posted on 11-07-2006 | 4,668 views

I've blogged before about short, SES style URLs and what I use. (To summarize - I'd either recommend URL Rewriting in Apache or IIRF for IIS.) What if you can't use a web server side solution? You're left with using a solution I used for BlogCFC, namely appending values after the file name and using CGI.PATH_INFO to parse it. Here is a simple example.

CGI.PATH_INFO is a CGI variable that will represent any information found after the filename in the URL. Look at the URL in the browser. Notice the index.cfm is followed by a / and then various bit of information. What I did for BlogCFC was create a specific pattern, and then wrote some code to parse that pattern and load various groups of blog entries (or A blog entry) based on the value. I could have also done something a bit more generic of the form:

/name1/var1/name2/var2

In this pattern, I've changed name1=var1&name2=var2 to a simpler list of name/value pairs. We can use the same pattern with Model-Glue, but since Model-Glue uses an event value, we need to preface it with the event to run. So the pattern I will use is:

/event/name1/var1/name2/var2

I opened up my Application.cfm file and added the following code:

view plain print about
1<!--- get the info --->
2<cfif len(trim(cgi.path_info))>
3    <!--- From Michael Dinowitz --->
4    <cfset urlVars=reReplaceNoCase(trim(cgi.path_info), '.+\.cfm/? *', '')>
5    <cfif urlVars is not "" and urlVars is not "/">
6        <!--- remove first / --->
7        <cfif left(urlVars, 1) is "/">
8            <cfset urlVars = right(urlVars, len(urlVars)-1)>
9        </cfif>
10        <!--- Event is first item --->
11        <cfset url.event = listFirst(urlVars, "/")>
12        <!--- strip it off --->
13        <cfset urlVars = listRest(urlVars, "/")>
14        <!--- now build name/val pairs --->
15        <cfloop index="x" from="1" to="#listlen(urlVars,"/")#" step="2">
16            <cfset name = listGetAt(urlVars, x, "/")>
17            <cfif listLen(urlVars, "/") gte x+1>
18                <cfset value = listGetAt(urlVars, x+1, "/")>
19            <cfelse>
20                <cfset value = "">
21            </cfif>
22            <cfset url[name] = value>
23        </cfloop>
24    </cfif>
25</cfif>

I'm not going to pick over this too much as you can see it is just string parsing. Basically I get the information after the /, grab the event as the first item, and if anything else is left than it is treated as name/value pairs. Also note that I support a name without a value. This would be useful for passing a "flag" type setting.

As a practical example, instead of using this URL:

/index.cfm?event=loadArticle&id=5

I can use this:

/index.cfm/loadArticle/id/5

For print format, I could use this:

/index.cfm/loadArticle/id/5/print

And to make it even nicer, I could include the title of the article in the URL. This would be used to help search engines, but have zero uses in the controllers:

/index.cfm/loadArticle/id/5/Dharma-Controls-All-Feed-The-Swan

Again - let me be clear - your best solution would be to use URL Rewriting, but this is an alternative you can use if that is not an option.

Comments

[Add Comment] [Subscribe to Comments]

That's more or less what I've been doing for almost a year now over at my site, though I don't think my code is quite as long. I like doing it that way because it's very flexible.
I recall there being an issue with IIS lockdown blocking these requests due to the period in what it initially believes is the directory name. Possibly addressed in newer versions (allowing a single period in a directory but rejecting two), I really don't know, but if anyone runs into a problem handling this request it may be relevant.
Michael,

I've never run into that problem with IIS, but I am running my development on an WindowsXP platform. Maybe it was an issue in older versions, but shouldn't be if you're up-to-date.
Just wondering about the 2nd cfif:

<cfif urlVars is not "" and urlVars is not "">

It looks like the same test twice.. ?
Despite the fact that IIRF is free, I would advice to go with ISAPI Rewrite. It's well worth the few $$. One of the main features that IIRF didn't have was the [U] directive -- unmangle logs files, meaning, log the request prior to the rewrite ... which was essential for me.
Um, it was a test Michael. ;)

The second condition was meant to be "/". Going to fix now. Thanks!
In the release notes, I do see a U directive Rob. So maybe he has added that in?
It may/probably have been added. I remember chatting with him when you first blogged about it and he said it wasn't difficult to add. Good to know; danke,
Very nice Ray. Got it implemented already! Love the use of the step attribute in cfloop.
Ok, now that I'm home I could get at my code. I've posted what I've been using on my blog here:

http://further.gregnilsen.com/index.cfm/module/sto...
I posted the code for how I do SES URLs on my (Fusebox) site back in September last year:

http://corfield.org/entry/SES__Bah_Humbug

It works with any framework (or non-framework) because it's included in Application.cfm (or Application.cfm).
I tried implementing the code into my Application.cfm file but it doesn't do anything. Am I missing something?
Chris - what do you see? You should also cfdump url and see if it is getting populated correctly.
Ray,

When I do the dump, it is getting populated. Do you call the url like this: #viewstate.getValue("myself")#theevent
No, you need to make the URLs yourself. Unless you make a helper function for it.
I tried using this code. It worked mostly. Where I have trouble is in the modelglue.xml file. When an event fires and I point to another event it seems to just the events together.

For example,
The link reads "index.cfm/page.login". The user clicks it and goes to the login form.

The login form submits to "/index.cfm/action.login" and fires an event "user.login", if successful the result from that event does "page.index". The resulting URL looks like "http://localhost/index.cfm/index.cfm?event=page.in...;

Any idea how to properly redirect in the result?
You said your first link was to index.cfm/page.login. It needs to be /index.cfm/page.login. Did you use that?
Thanks Ray, that was part of my problem.

There is one other thing. In a result, depending on success or failure, I fire another event. When I use "redirect=true" (<result name="success" do="page.index" redirect="true" />), the URL gets messed up again to http://localhost/index.cfm/index.cfm?event=page.in...

It works if I leave the redirect part out. I have tried different things in the "do" (/index.cfm/page.index, /page.index) part but I get errors.

Is this just the way this works?
@Ross, yes, redirects use regular URLs. That's how it's built inside Model-Glue. Since people are unlikely to bookmark the result of a form post (which is when you mostly use redirect="true"), I don't see that as a big problem.

[Add Comment] [Subscribe to Comments]