URL Rewriting in ASP.NET
Web sites often receive most of the traffic from search engines, like Google. Because of that it is very important to optimize web pages for search engines to rank higher in their search results. With better position web page receives more traffic since visitors usually look only at first page and rarely list all results. There are hundreds of factors that affect ranking. One of most important is to have keywords in URL of the page. Dynamically created sites usually don't contain keywords in URL, but use query strings, like this /ShowArticle.aspx?id=31231&category=43. This is OK if you look programming logic, pages that shows article is named ShowArticle.aspx. But it is not search engines friendly and you can't say what is on page, except that shows some article with primary key equal to some number.
So, the basic idea is to show to visitors and web spiders link in form like /Category-Name/Rich-Keywords-Article-Name/ , while internally execute ShowArticle.aspx?id=22342342&category=23. Another reason for translating URLs is usability. URLs with query strings are not memorable and not user friendly. Users prefer shorter URLs with meaningful keywords. One more reason for URL rewriting could be if you changing application's directory structure but you don't want to lose incoming links from other sites. In this case, everything will stay same for outside world, but your application will work differently. Also, there is some security risk if you expose your query string variables to visitors.
These are pretty common requests, and during the time a lot of different solutions are developed. The result of all methods should be the same: If you look from outside, visitor or search engine will see only a normal page with keywords in URL, but web server will execute page with query strings.
URL rewriting in web.config
This type of URL rewriting could be used only for a small number of pages. You need to rewrite every page manually in web.config's urlMappings section. This example rewrites five URLs:
<?xml version="1.0"?>
<configuration>
<urlMappings enabled="true">
<add url="~/News.aspx"
mappedUrl="~/Default.aspx?id=1" />
<add url="~/Products.aspx"
mappedUrl="~/Default.aspx?id=2" />
<add url="~/Articles.aspx"
mappedUrl="~/Default.aspx?id=3" />
<add url="~/Resources.aspx"
mappedUrl="~/Default.aspx?id=4" />
<add url="~/About-Us.aspx"
mappedUrl="~/Default.aspx?id=5" />
</urlMappings>
. . .
</configuration>
As you see, although using of this method is pretty simple, you can't use it for rewriting 100 000 or more dynamic pages. URL rewriting in web.config is simple and easy, but in the same time useless in real scenario. This is new ASP.NET feature. Hopefully, URL mapping will be improved in next versions of ASP.NET to include variables, regular expressions etc. Current URL mapping allows only static links which makes it very limited in use. Notice that ASP.NET 1.1 doesn't support <urlMappings> element in web.config, this option is available from ASP.NET 2.0.
URL rewriting in Global.asax
URL rewriting using Global.asax file is also very simple, but in the same time pretty useful too. You need to use Application_BeginRequest event. Inside procedure, use Context.RewritePath method to specify which URL will execute in behind. Here is a code:
[ C# ]
void Application_BeginRequest(object sender, EventArgs e)
{
// Get current path
string CurrentPath = Request.Path.ToLower();
// Now we'll try to rewrite URL in form of example format:
// /Articles/Article-Keywords-Rich-Slug/
// Check if URL needs rewriting, other URLs will be ignored
if (CurrentPath.StartsWith("/articles/"))
{
// I will use simple string manipulation, but depending
// of your case, you can use regular expressions instead
// Remove / character from start and beginning
CurrentPath = CurrentPath.Trim("/");
string ArticleSlug = CurrentPath.Substring(CurrentPath.IndexOf("/"));
// Rewrite URL to use query strings
HttpContext MyContext = HttpContext.Current;
MyContext.RewritePath("/Show-Article.aspx?slug=" + ArticleSlug);
}
}
[ VB.NET ]
<%@ Application Language="VB" %>
<script RunAt="server">
Protected Overloads Sub Application_BeginRequest(ByVal sender As Object, ByVal e As System.EventArgs)
' Get current path
Dim CurrentPath As String = Request.Path.ToLower()
' Now we'll try rewrite URL in form of example format
' /Articles/Article-Keywords-Rich-Slug/
' Check if URL needs rewriting, other URLs will be ignored
If CurrentPath.StartsWith("/articles/") Then
' I will use simple string manipulation, but depending
' of your case, you can use regular expressions instead
' Remove / character from start and beginning
CurrentPath = CurrentPath.Trim("/")
Dim ArticleSlug As String = CurrentPath.Substring(CurrentPath.IndexOf("/"))
' Rewrite URL to use query strings
Dim MyContext As HttpContext = HttpContext.Current
MyContext.RewritePath("/Show-Article.aspx?slug=" & ArticleSlug)
End If
End Sub
</script>
This example uses slug as query string parameter. Because of that, slug needs to be unique for every page. If you find this difficult or not appropriate for your case, another option is to add ID at the end of the URL, then extract it from string to some ArticleID variable, and then rewrite path to something like RealPath = "/Show-Article.aspx?id=" + ArticleID.
By using Application_BeginRequest method you can experience some difficulties, depending of which version of IIS you use and what are settings used. In some scenarios IIS will not activate ASP.NET if page not exists. In that case instead of URL rewriting you'll see just 404 Not Found error. Solution is to set IIS to inform ASP.NET about web request even if file not exists. On the web site level, click Configuration button for application settings and find "Check if file exists" check box. If you are on shared hosting and provider would not change settings, you still can use some of next methods like ISAPI or ASP.NET Routing.
URL rewriting with HttpModule
URL rewriting with HttpModule was one of the best methods to implement URL rewriting until Routing is introduced. HttpModules are reusable and can be configured easily. To learn generally about HttpModules and how to create one check How To Create Your Own Http Module tutorial.
To create HttpModule, open Visual Studio and start new project of type Class Library. Add reference to System.Web namespace (in Solution Explorer window, right click to References, choose Add Referenc... and add System.Web namespace from .Net tab).
Every HttpModule needs to implement IHttpModule interface, so it must have Init and Dispose methods. Change file name of the "Class1" to "UrlRewriting" and add this code:
[ C# ]
using System;
using System.Web;
namespace UrlRewriteHttpModule
{
public class UrlRewriting : IHttpModule
{
/// <summary>
/// Initialization of HttpModule
/// This method is required for IHttpModule interface
/// </summary>
/// <param name="MyApp"></param>
public void Init(System.Web.HttpApplication MyApp)
{
// Connect module function with Application's BeginRequest event
MyApp.BeginRequest += new System.EventHandler(Rewriting_BeginRequest);
}
/// <summary>
/// Write dispose method if you need
/// It must be declared because it is required by
/// IhttpModule interface
/// </summary>
public void Dispose()
{
// Our example is simple and we don't need to do
// anything here
}
/// <summary>
/// Here we'll actually rewrite URLs
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void Rewriting_BeginRequest(object sender, System.EventArgs args)
{
// Get reference of current web application
HttpApplication MyApp = (HttpApplication)sender;
// Get original page address
string MyOldPath = MyApp.Request.Path;
// Do some simple or more complex processing
if (MyOldPath == "/Products/")
{
// And finally rewrite URL
MyApp.Context.RewritePath("/ShowCategory.aspx?name=Products");
}
}
}
}
[ VB.NET ]
Imports System
Imports System.Web
Namespace UrlRewriteHttpModule
Public Class UrlRewriting
Implements IHttpModule
''' <summary>
''' Initialization of HttpModule
''' This method is required for IHttpModule interface
''' </summary>
''' <param name="MyApp"></param>
Public Sub Init(ByVal MyApp As HttpApplication)
' Connect module function with Application's BeginRequest event
MyApp.BeginRequest += New System.EventHandler(Rewriting_BeginRequest)
End Sub
''' <summary>
''' Write dispose method if you need
''' It must be declared because it is required by
''' IhttpModule interface
''' </summary>
Public Sub Dispose()
' Our example is simple and we don't need to do
' anything here
End Sub
''' <summary>
''' Here we'll actually rewrite URLs
''' </summary>
''' <param name="sender"></param>
''' <param name="args"></param>
Public Sub Rewriting_BeginRequest(ByVal sender As Object, ByVal args As EventArgs)
' Get reference of current web application
Dim MyApp As HttpApplication = sender
' Get original page address
Dim MyOldPath As String = MyApp.Request.Path
' Do some simple or more complex processing
If MyOldPath = "/Products/" Then
' And finally rewrite URL
MyApp.Context.RewritePath("/ShowCategory.aspx?name=Products")
End If
End Sub
End Class
End Namespace
Build a project and then copy assembly to /bin folder of your web application. To make it work, you need to register it in web.config like any other HttpModule:
<system.web>
<httpModules>
<add type="UrlRewriteHttpModule.UrlRewriting,UrlRewriteHttpModule" name="UrlRewriteHttpModule" />
</httpModules>
</system.web>
URL rewriting is done in module's Rewriting_BeginRequest method. First, code creates a reference of current HttpApplication and then get original URL with Request.Path or Request.URL methods. After processing of the URL, use Context.RewritePath method to rewrite URL to new value. As you see, idea of URL rewriting with Context.Rewrite path method is completely the same like in previous example of URL rewriting in Global.asax. That means that you also could experience same problems, like 404 File Not Found error. So, if that happened, you should configure IIS to call ASP.NET even if requested file not exits.
URL processing in this example is extremely simple. To make it more useful, you can create rewrite rules in web.config to add new rules without recompiling HttpModule assembly. Next logical step could be to support regular expressions to make rewrite rules more powerful.
You can enhance module on many ways, but every new feature takes time for coding and testing. Fortunately, there are two free, mature HttpModule solutions: UrlRewriter.Net and UrlRewrting.Net, with many features to make programmer's life easier.
URL rewriting with UrlRewriter.Net and UrlRewriting.Net Http Modules
Instead of building your custom HttpModule for rewriting, you can use complete HttpModule solutions. Two most popular are UrlRewriter.Net and UrlRewriting.Net. It looks like UrlRewriter.Net site is not active any more, but you can download their 1.6 version from http://www.brothersoft.com/urlrewriter.net-117841.html. Some popular ASP.NET applications, like Yet Another Forum .Net uses UrlRewriter.Net.
Second available solution is UrlRewriting.Net. Which module is better depends mostly of your preferences; technically both use same method to rewrite URLs. These modules work in medium trust (common in shared hosting), supports themes and master pages, regular expressions, post backs etc. The most important advantage of UrlRewriting.Net is easy creation of rules at run time.
How To Implement URL rewriting with UrlRewriter.Net?
To implement URL rewriting with UrlRewriter.Net follow these steps:
1. Download UrlRewriter.Net, it looks that their site is no longer active, you can still use http://www.brothersoft.com/urlrewriter.net-117841.html (source code also available).
2. Copy Intelligencia.UrlRewriter.dll to your web applicaton's /bin folder and add reference to it.
3. Edit web.config file, register UrlRewriter in httpModules section:
<httpModules>
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter" />
</httpModules>
<validation validateIntegratedModeConfiguration="false" />
4. In configSections, add new section named rewriter, like this:
<configSections>
<section name="rewriter"
requirePermission="false"
type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
</configSections>
5. Finally add <rewriter > section, we'll write example rewrite rule here. Place this code before </configuration>:
<rewriter>
<rewrite url="~/Products/(.*).aspx" to="~/ShowProduct.aspx?Slug=$1"/>
</rewriter>
As you see, you can use regular expressions to get more flexible rules. In this case, requested page name is transferred to page /ShowProduct.aspx that uses query string variable.
How To Implement URL rewriting with UrlRewriting.Net?
To implement URL rewriting with UrlRewriting.Net just follow these simple steps:
1. Download product from their site, extract it somewhere on local disc and copy UrlRewritingNet.UrlRewriter.dll to /bin colder of web application.
2. Set up web.config, add new section to configSection node, and urlrewritingnew tag bellow </configsections> but before </configuration>:
<configuration>
<configSections>
<section name="urlrewritingnet"
restartOnExternalChanges="true"
requirePermission ="false"
type="UrlRewritingNet.Configuration.UrlRewriteSection,
UrlRewritingNet.UrlRewriter" />
</configSections>
...
<urlrewritingnet
rewriteOnlyVirtualUrls="true"
contextItemsPrefix="QueryString"
defaultPage = "default.aspx"
defaultProvider="RegEx"
xmlns="http://www.urlrewriting.net/schemas/config/2006/07" >
</urlrewritingnet>
</configuration>
3. Register assembly as HttpModule inside httpModules section: <system.web>
<httpModules>
<add name="UrlRewriteModule"
type="UrlRewritingNet.Web.UrlRewriteModule, UrlRewritingNet.UrlRewriter" />
</httpModules>
</system.web>
4. Inside created urlrewritingnet node in step 3, add <rewrites > sub tag. Inside <rewrites > tag, use tag <add > to add rewrite rules. You can add as many rules as you want. Example rewrite rule could look like this:
<rewrites>
<add name="Rewrite" virtualUrl="^~/(.*)/Detail(.*).aspx"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="~/Default.aspx?language=$1&id=$2"
ignoreCase="true" />
</rewrites>
Rewrite rule syntax is pretty simple. Notice two (.*) parts in virtualUrl attribute. These URL parts are replaced with $1 and $2 in destinationUrl attribute. Of course, you can use $3, $4 etc. if you need it. Personally, if case allows it, I prefer to add just one rule that uses article slug as parameter. If you build large application and need many rules, make your rules specific enough to avoid possible overlapping between them.
For more details about rules and using of UrlRewriting.Net, check their documentation on main site.
Postback problem with URL rewriting
As you see, it is pretty easy to implement URL rewriting. But, be aware, it is not that easy :). Web form uses real URL in action attribute. So, if visitor submits a form nice rewritten URL will be lost and in browser's address bar real URL with query strings will appear. To keep rewritten URLs after postbacks you need to change form's action attribute. You can do it in Form_Load procedure, with code like this (available in ASP.NET 3.5 SP 1 or above):
[ C# ]
protected void Page_Load(object sender, EventArgs e)
{
form1.Action = Request.RawUrl;
}
[ VB.NET ]
Protected Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles form1.Load
form1.Action = Request.RawUrl
End Sub
You can add this code to master page to avoid repetitive code.
If your web application uses earlier version than ASP.NET 3.5 SP1, you can create a form class that inherits from standard form and change action attribute on that way.
[ C# ]
using System.Web.UI;
public class RewritingForm : HtmlForm
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
// write attribute name
writer.WriteAttribute("name", this.Name);
base.Attributes.Remove("name");
// write attribute method
writer.WriteAttribute("method", this.Method);
base.Attributes.Remove("method");
this.Attributes.Render(writer);
// get new value of action attribute
String action = Context.Request.RawUrl;
// write attribute action
if (action != null)
writer.WriteAttribute("action", action);
base.Attributes.Remove("action");
// write id if exists
if (base.ID != null)
writer.WriteAttribute("id", base.ClientID);
}
}
[ VB.NET ]
Imports System.Web.UI
Public Class RewritingForm
Inherits HtmlForm
Protected Overrides Sub RenderAttributes(ByVal writer As HtmlTextWriter)
' write attribute name
writer.WriteAttribute("name", this.Name)
base.Attributes.Remove("name")
' write attribute method
writer.WriteAttribute("method", this.Method)
base.Attributes.Remove("method")
this.Attributes.Render(writer)
' get new value of action attribute
Dim action As String = Context.Request.RawUrl
' write attribute action
If action Is Not Nothing Then
writer.WriteAttribute("action", action)
End If
base.Attributes.Remove("action")
' write id if exists
If base.ID Is Not Nothing Then
writer.WriteAttribute("id", base.ClientID)
End If
End Sub
End Class
Use this control as any other custom control instead of <form runat="server"> tag. To learn more about creating custom controls and how to place it on ASP.NET page see Custom Server Controls In ASP.NET. Another option is to use JavaScript and change form tag attribute after form is rendered. You can use jQuery to simplify this task.
Relative link problem with URL rewriting
If you use relative links (e.g. for themes, .css or .js files), these could get broken after you implement URL rewriting. If you need relative links, possible solution is to use second override of Context.RewriteUrl method and set RebaseClientPath to false:
[ C# ]
Context.RewritePath("/Real-Path-With-QueryStrings.aspx?id=23423423", false);
[ VB.NET ]
Context.RewritePath("/Real-Path-With-QueryStrings.aspx?id=23423423", false);
URL rewriting with ISAPI filter
URL rewriting in Global.asax or HttpModule works practically on the same way. There are few drawbacks when using these methods:
- by default, web server will return 404 page not found error, so you need to configure IIS to use ASP.NET even if requested resource doesn't exist
- you can rewrite only .aspx pages. Alternative is to configure IIS to use ASP.NET for every file type, but this approach also can slow down your server and waste resources
ISAPI URL rewriting has advantage because it rewrites URL before it comes to ASP.NET. URL is rewritten as soon as request comes to IIS. Although this solution is not free, it is worthy to check commercial solution like Helicon ISAPI_Rewrite. For only $99 you get reliable, standardized and mature solution that avoids common URL rewriting problems with Global.asax or HttpModule. ISAPI_rewrite enables control over all kinds of links, including static HTML files, images, documents, ASP.NET 1.1 or classic ASP pages etc.
Conclusion
Notice that existing incoming links to your web site will stay the same and visitors from refering sites will look for old pages, as they looked before rewriting. You need to provide permanent redirection (301) from old URL to new one. ASP.NET by default do temporal redirect (returns code 302) which splits your ranking on search engines to two URLs. Better SEO solution is to redirect permanently and send code 301. On this way, all ranking will go to your new link and old bookmarked link will still work. To find out how to implement 301 redirection check How to redirect page permanently with status code 301? tutorial.
In the past, Apache users had advantage with mod_rewrite module. Today, ASP.NET programmers have several good options for URL rewrtiing, presented in this tutorial. IIS 7 is adding new features for manipulating URLs. Also, now we get ASP.NET Routing that origin from ASP.NET MVC. More about Routing and IIS 7 URL manipulation read in some next tutorial.
Source :- http://www.beansoftware.com/ASP.NET-Tutorials/URL-Rewriting-ASP.NET.aspx
No comments:
Post a Comment