Phil Haacked recently posted on the ability to use REST style URL's with ASP.NET MVC.
So what's IMHO the main aspect of the MVC framework? It uses a REST-like approach to ASP.NET Web development. It implements each request to the Web server as an HTTP call to something that can be logically described as a "remote service endpoint". The target URL contains all that is needed to identify the controller that will process the request up to generating the response--whatever response format you need
The ASP.NET MVC framework is still months away from release, so I have been writing CodeBlog (Yeah, thats the temporary name of my little blog engine) using standard 3-tiered design with WebForms for the presentation layer.
However, I still wanted to take advantage of REST style url's, even if I was largely faking it.
This means that I want:
http://yourdomain.com/ViewSinglePost.aspx?PostId=1241414
To become:
http://yourdomain.com/Posts/ThisIsAExamplePost/
To do URL Rewriting on ASP.NET usually requires the installation of a ISAPI module, requiring Admin access to IIS. I want to run CodeBlog on a shared server. This forces me to implement URL Rewriting entirely in my code.
I solved this problem by creating a HTTPModule that contains a Application_BeginRequest event handler. This is called for each request to the ASP.NET service.
A crucial flaw of this design became obvious when I tested the HTTPModule under IIS 5.1 (Yeah, I'm still running XP here. I really need to upgrade.)
IIS automatically serves the default 404 error without invoking ASP.NET, so the Application_BeginRequest event is never fired. This can be fixed by assigning a ASPX page to IIS's 404 Error mapping.

Or, in the case of a shared server, just setting the default 404 Error page to point to an ASPX file. This can usually be done within the hosts control panel.
Creating a 404.aspx file allows you to generate a nice clean custom error if you really do need to serve a 404 error, so I recommend doing this even if you do not use URL Rewriting.
With that problem solved, the HTTPModule was intercepting and transferring URL's without a hitch. The full code of the HTTPModule is below:
1 using System;
2 using System.Web;
3 using System.Collections;
4 using CodeBlog.BOL;
5
6 namespace CodeBlog.HttpModules
7 {
8 public class UrlRewrite : IHttpModule
9 {
10
11 public void Init(HttpApplication application)
12 {
13 application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
14
15 }
16
17 private void Application_BeginRequest(Object source, EventArgs e)
18 {
19 // The RawUrl will look like:
20 // http://domain.com/404.aspx;http://domain.com/Posts/SomePost/
21
22 if (HttpContext.Current.Request.RawUrl.Contains(";")
23 && HttpContext.Current.Request.RawUrl.Contains("404.aspx"))
24 {
25 // This places the originally entered URL into url[1]
26 string[] url = HttpContext.Current.Request.RawUrl.ToString().Split(';');
27
28 // Element 0-2 of this string array are the domain.
29 string[] urlInfo = url[1].Split('/');
30 if (urlInfo.Length > 2)
31 {
32 // Element 3 defines the action
33 string action = urlInfo[3];
34
35 // In this case, we only care about one action: POSTS
36
37 if (action.ToUpper() == "POSTS" && urlInfo.Length > 4)
38 {
39 // The key value is passed after posts...IE : Posts/SomePost/
40 string key = urlInfo[4];
41
42 // This code verifies that there is a matching Post in the database
43 // by calling our handy static Business Object PostService.
44 int postId = PostService.GetPostIdFromUrlKey(key);
45
46 if (postId != int.MinValue)
47 {
48 // Using Server.Transfer changes the response to
49 // the new page without letting the client know, so the fake URL remains.
50 Server.Transfer("/ViewSinglePost.aspx?PostId=" + postId.ToString());
51 }
52 else
53 {
54 // No Matching Post was found.
55 HttpContext.Current.Response.Redirect("/NoPostFound.aspx");
56 }
57 }
58 }
59 }
60
61 // If we get here, then the actual contents of 404.aspx will get loaded.
62 }
63
64 public void Dispose()
65 {
66 // Needed for implementing the interface IHttpModule.
67 }
68 }
69 }
This module is compiled into its own class project and included as a reference in my main CodeBlog project. In the case of doing real URL Rewriting, you can define rewrite rules in that Application_BeginRequest method, instead of just faking REST with a quick post lookup.
To use a HTTPModule, you just need to add a few lines to your web.config:
1 <httpModules>
2 <add name="UrlRewrite" type="CodeBlog.HttpModules.UrlRewrite" />
3 </httpModules>
Hopefully this helps a few people out who are trying to implement Url Rewriting on shared hosts, or on older versions of IIS.
17 comments:
What's wrong with this solution?
Anonymous:
From the FAQ on that page:
04. Are Rewrites possible without a Fileextension?
No, this is not possible without changing the IIS configuration because these requests are directly not handled by ASP.NET 2.0
Yes if you can change the IIS Configuration, take a look the UrlRewritingNet documentation.
This means no REST friendly urls, just stuff like:
SomeDomain.com/Posts/ThisIsASamplePost.aspx
You solution works ONLY if IIS is setup to route ANY request to the asp.net extension. On a shared host, only the host can do this mapping so it depends on your luck.
Anonymous:
By pointing IIS's 404 page to a ASPX, you are invoking the ASP.NET service.
This can be done on most shared hosts by simply pointing the custom error page to a ASPX file.
It's an interesting solution then.
I tried catching the 404 in the global.asax... worked perfectly on casini, not in IIS on a Win2003 box.. it would catch application errors but not 404's
Hi Paul,
Did you remember to map the 404 page in IIS to an aspx.
I have not tested this with IIS 6.0 yet, just 5.1 and Cassini, however I know others have been able to use this method with IIS 6.0.
You may have to configure the wildcard mapping.
@Paul
You need to change this at the IIS level of settings. In a hosted environment it is likely in the custom control panel (it is viable in most places). Otherwise it is a part of the IIS control panel settings. You need to do it there... Cassini works because every request is piped through it.
Here's some code I wrote that does Url Rewriting/Mapping with Regular Expression support: http://pietschsoft.com/Blog/Post.aspx?PostID=762
Hi Jonathan,
I like the idea of the custom 404.aspx to ensure the ISAPI redirects to the ASP Worker Process. I'm just wondering if you then need a hack to check for the real 404's from the normal urls? Thanks for the post.
Hi Brig,
If the HTTPModule does not match up the URL with any rules, then it falls out of the if block, and the 404 page is loaded.
i have been using this approach and works just dandy, although.
in my case i use it in conjuction of the webparts framework.
my problem is thatwhen i try to edot the page i get a 405 error form the server.
any hints
Hi David,
ScottGu explains how to use a Control Adapter to fix form postbacks when rewriting URLS here:
http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
Great article! Thx
Best regards from Russia
much simpler solution exist.
The approach with the forms works, but it never does if you need postbacks in your page.
i thnk is because the 404 doesnt allow POST
welcome to the wow power leveling cheap service site, buy wow power leveling cheap wow gold,wow gold,world of warcraft power leveling buy wow gold
Post a Comment