IIS 7.0 Extensionless UrlRewriting (Short urls)

How to use the managed IIS UrlRewrite Module with custom providers

  • To be able to perform extensionless UrlRewriting in IIS 7.0, you can install the managed code UrlRewrite modules, which can be downloaded from here, and the UrlRewrite Extensibility samples here.

    These provider samples include managed code that:

    Store the rewrite or redirect mappings in a SQL database;

    Store the rewrite or redirect mappings in a text file;

    Store the lookup substrings in a text file.

    The one we'll work with here is the DB provider - which looks up an extensionless URL in a SQL Server database that you configure, and redirects the request to the final url that it is mapped to. So for example, if you have a database table entry that maps "http://localhost/aXhf3" to "http://yahoo.com", a request to http://localhost/aXhf3 would actually get redirected to Yahoo's home page -- it does not matter whether the end url is on or off site; the target URL can be absolute or relative as stored in the lookup row.

    The samples include a script that creates a new database, the single table, and one stored procedure that accepts a single NVARCHAR input parameter, and returns a single -row, single column result consisting of the looked-up target url. I've modified this to make the query case sensitive - which you would want if you are using a custom cryptographic key generator to generate short urls. This allows for millions of cryptographically unique keys that are case sensitive, meaning that a request for "axhf3" could redirect to a different url than the one illustrated above.

    The samples provide the source code for each of the mentioned providers, but it is not necessary to alter the code at all. Here is the original SQL Script that I've modified to perform a case-sensitive search, and changed one of the entries to accept a "short url" key:

    USE [master]
    CREATE LOGIN [IIS APPPOOL\DefaultAppPool] FROM WINDOWS WITH DEFAULT_DATABASE=[master]

    CREATE DATABASE [RewriteDB]
    GO

    USE [RewriteDB]
    GO

    CREATE TABLE [dbo].[RewriteTable](
    [OriginalUrl] [
    nvarchar](256) NOT NULL,
    [NewUrl] [
    nvarchar](256) NOT NULL
    )
    ON [PRIMARY]
    GO

    CREATE PROCEDURE [dbo].[GetRewrittenUrl]
    @
    input nvarchar(256)
    AS
    SELECT rt.NewUrl
    FROM dbo.RewriteTable rt
    WHERE rt.OriginalUrl = @input COLLATE SQL_Latin1_General_CP1_CS_AS
    GO

    CREATE USER [IIS APPPOOL\DefaultAppPool] FOR LOGIN [IIS APPPOOL\DefaultAppPool]

    GRANT EXECUTE ON dbo.GetRewrittenUrl TO [IIS APPPOOL\DefaultAppPool];
    GO

    INSERT INTO dbo.RewriteTable VALUES ('abcdef', 'http://yahoo.com');
    INSERT INTO dbo.RewriteTable VALUES ('old/contactus/index.html', 'new/contactus.html');
    GO

    The next step is to configure your IIS mappings. In IIS Manager for the particular web site, open the UrlRewrite feature, and select "View Providers" in the Actions pane. Then select "Add Provider" from the Actions pane.

    In the Add Provider dialog, put in the name you want to use when the provider is referred to by a rule (in this case, "DB") and then choose the "DbProvider, Microsoft.Web.Iis.Rewrite.Providers, Version=7.1.761.0, Culture=neutral, PublicKeyToken=0545b0627da60a5f" provider from the lower Managed Types dropdown. This dropdown takes a VERY long time to load, so be patient.



    Now that you've commited your selected provider, you want to Add two provider settings, the connection string, and the name of the stored proc. These would be like the following for the sample:

    SQL Server Connection String: server=(local);database=RewriteDb;Integrated Security=True
    Stored procedure name: GetRewrittenUrl

    With the above completed, the only remaining item to add is the rewrite rule in the web.config. Here's a completed web.config with the rule section added in:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    <!-- Download the IIS 7.0 UrlRewrite module: http://www.iis.net/download/URLRewrite -->
    <system.webServer>
    <rewrite>
    <providers>
    <!-- Note: provider was already registered via IIS Manager -->
    </providers>
    <rules>
    <rule name="DbProviderTest" stopProcessing="true">
    <match url="(.*)" />
    <conditions>
    <add input="{DB:{R:1}}" pattern="(.+)" />
    </conditions>
    <action type="Redirect" url="{C:1}" />
    </rule>
    </rules>
    </rewrite>
    <handlers accessPolicy="Read, Execute, Script">
    <remove name="StaticFile" />
    <add name="StaticFile" path="*" verb="GET,POST" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="File" requireAccess="Script" />
    </handlers>
    <httpErrors>
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" prefixLanguageFilePath="" path="Extensionless/404.aspx" responseMode="ExecuteURL" />
    </httpErrors>
    <tracing>
    <traceFailedRequests>
    <add path="*">
    <traceAreas>
    <add provider="ASP" verbosity="Verbose" />
    <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
    <add provider="ISAPI Extension" verbosity="Verbose" />
    <add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,FastCGI" verbosity="Verbose" />
    </traceAreas>
    <failureDefinitions statusCodes="404" />
    </add>
    </traceFailedRequests>
    </tracing>
    </system.webServer>
    <system.web>
    <authentication mode="Windows" />
    <identity impersonate="true" />
    </system.web>
    </configuration>

    Note above that the <add input="{DB: ... refers to the provider name we chose earlier of "DB".
    The pattern just says, "Take any url, and look it up in the table, and redirect the user to the entry we have for the target url in the table."

    To test the rule open a web browser and make a request to http://localhost/abcdef and you should be redirected to the Yahoo home page. If you make a request for http://localhost/aBcdef, you should get a 404 page since we've configured extensionless urlrewriting to be case-sensitive in the database lookup.

    The only other thing missing is a way to generate cryptographically unique "short" url strings of say 4, 5 or six characters. The code I use to do that looks like this:

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Security.Cryptography;
    using System.Text;

    namespace MySite
    {
    public class UniqueId
    {
    public static string GetUniqueKey()
    {
    int maxSize = 4;
    char[] chars = new char[62];
    string a;
    a =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    chars = a.
    ToCharArray();
    int size = maxSize;
    byte[] data = new byte[1];
    RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
    crypto.
    GetNonZeroBytes(data);
    size = maxSize;
    data =
    new byte[size];
    crypto.
    GetNonZeroBytes(data);
    StringBuilder result = new StringBuilder(size);
    foreach (byte b in data)
    { result.
    Append(chars[b % (chars.Length - 1)]); }
    return result.ToString();
    }
    }
    }

    About Redirects:

    There is often some misconception about whether redirects will "mess up" your "link juice" with the search engines.

    There are two types of redirects you can use, a 301 and a 302. These numbers refer to the HTTP Status Code returned by the server for a given URL. A 301 redirect tells the search engine that the page has moved permanently to the new URL. A 302 redirect tells the search engine that the move is only temporary, and you may decide to show content at the original location in the future without a redirect.

    301 Redirects
    All three major search engines handle 301 redirects the same way; they ignore the original URL and instead they index the destination URL.

    301 redirects can be very powerful when you redesign your site and the URLs change, move to a different domain, acquire a new domain, or implement a URL rewrite. In most cases, this is the type of redirect you want to use because you know exactly how the search engines will respond. 301 is the type of redirect used here with the short URL mechanism.

    To perform a 301 Redirect in ASP.NET use code similar to the following:

    private void Page_Load(object sender, System.EventArgs e)
    {
    Response.Status = "301 Moved Permanently";
    Response.AddHeader("Location","http://www.newsite.com/newpage");
    }


    You can also do this in Global.asax or in an HTTP handler (ashx) or an HTTPModule:

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
    string sOldPath = HttpContext.Current.Request.Path.ToLower();
    string sPage = "http://www.newsite.com/" + sOldPath;
    Response.Clear();
    Response.Status = "301 Moved Permanently";
    Response.AddHeader("Location",sPage);
    Response.End();
    }

    You can also do this via IIS:

    IIS Redirect

    In internet services manager, right click on the file or folder you wish to redirect
    Select the radio titled "a redirection to a URL".
    Enter the redirection page
    Check "The exact url entered above" and the "A permanent redirection for this resource"
    Click on 'Apply


    Here is example Fiddler output for the 301 Yahoo redirect that we're performing in this demo:

    Result Protocol Host URL Body Caching Content-Type Process Comments Custom
    301 HTTP yahoo.com 211 no-store text/html iexplore:4476


    With all these pieces in place, you're all set to create your own extensionless short url rewriting masterpiece!

By Peter Bromberg   Popularity  (7081 Views)