Creating Rewrite Maps for Redirecting Old DasBlog URLs After Migration

Rewrite Maps in IIS

After successfully migrating the content from the old DasBlog site to the new DocPad based one, it was time to generate permanent redirects of old URLs to new ones, to keep inbound links working and not lose search rankings, once the new site goes live. Since the site is going to be hosted in Azure, I decided to use the URL Rewrite module - rewrite maps to be exact; because I need to map a large number of individual URLs, which can't be covered by a generic rule.

To avoid cluttering the web.config file with all the mappings, I moved the rewrite maps into a separate file, keeping in web.config only the rules and a reference to the external file:

<configuration>
  <system.webServer>
    <rewrite>
      <rewriteMaps configSource="rewriteMaps.config" />
      <rules>
        <rule name="commentLinks">
          <match url="^CommentView,guid,.+\.aspx$" />
          <conditions>
            <add input="{commentLinks:{REQUEST_URI}}" pattern="(.+)" />
          </conditions>
          <action type="Redirect" url="{C:1}" appendQueryString="false" 
                  redirectType="Permanent" />
        </rule>
        <rule name="permalinks">
          <match url=".+\.aspx$" />
          <conditions>
            <add input="{permalinks:{REQUEST_URI}}" pattern="(.+)" />
          </conditions>
          <action type="Redirect" url="{C:1}" appendQueryString="false" 
                  redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

In the snippet above I have two rewrite rules for two rewrite maps. Matching their names; the first one takes care of GUID based comment pages, while the second one covers the article permalinks. Here's a valid snippet of rewriteMaps.config file:

<rewriteMaps>
  <rewriteMap name="permalinks">
    <add key="/BookReviewSignalRRealtimeApplicationCookbook.aspx" 
         value="/blog/posts/20150626-BookReviewSignalRRealTimeApplicationCookbook.html"/>
    <add key="/BookReviewMasteringTypeScript.aspx" 
         value="/blog/posts/20150615-BookReviewMasteringTypeScript.html"/>
  </rewriteMap>
  <rewriteMap name="commentLinks">
    <add key="/CommentView,guid,50cee933-b7e1-4a15-99a4-a70b69d07dbd.aspx" 
         value="/blog/posts/20150626-BookReviewSignalRRealTimeApplicationCookbook.html"/>
    <add key="/CommentView,guid,3fe215bf-3019-48fc-a765-bb4e3892fdd0.aspx" 
         value="/blog/posts/20150615-BookReviewMasteringTypeScript.html"/>
  </rewriteMap>
</rewriteMaps>

As long as rewriteMaps is the root element, and its children would be valid directly in web.config, everything should work fine. Also notice that rewrite map names should match the input attribute value in rule conditions.

Generating Rewrite Maps

Considering the large number of posts on my site, I had no intention of writing the rewrite maps by hand. Instead, I took advantage of the BlogML export of DasBlog content which I already used to convert the posts to the new format. Of course I also used the same tooling: Grunt and CoffeeScript. I just added the rewrite map generation to the conversion process.

First, I created two associative arrays with the URL mappings:

exportRewriteMaps = (posts) ->
  fs = require 'fs'
  moment = require 'moment'
  slug = require 'slug'
  titleCase = require 'title-case'

  permalinkMappings = {}
  commentLinkMappings = {}

  for post in posts
    newUrl = moment(post.$['date-created']).format('YYYYMMDD') + '-' + 
      slug(titleCase(post.title[0]._), '') + '.html'
    permalinkMappings[post.$['post-url'].replace('http://www.damirscorner.com/', '')] = newUrl
    commentLinkMappings['CommentView,guid,' + post.$['id'] + '.aspx'] = newUrl

The posts argument matches the post collection I used for conversion. Of course, I generate newUrl the same way I generated the filename for converted blog posts, with the omission of .md extension which is removed by DocPad when generating the site. Post permalinks are stored in post-url attribute; I just make them relative by removing the hostname part. I recreate the comment links in the code; I use id attribute which contains the post GUID.

Now I had everything I needed to export the rewrite maps to a file:

xmlBuilder = require 'xmlbuilder'

root = xmlBuilder.create('rewriteMaps')

createRewriteMap 'permalinks', permalinkMappings, root
createRewriteMap 'commentLinks', commentLinkMappings, root

fs.writeFileSync 'rewriteMaps.config', root.end { pretty: true }

I use the xmlbuilder-js package. I create the required rewriteMaps root element and pass it to the createRewriteMap function which generates the rewriteMap element corresponding to the other function arguments. In the end I dump everything into the destination file. The missing function is pretty straightforward:

createRewriteMap = (name, mappings, root) ->
  map = root.ele('rewriteMap',
    name: name)

  map.ele('add',
    key: '/' + key
    value: '/blog/posts/' + value) for key, value of mappings

The only part worth mentioning, is the correct folder prefix that's added to generated new post file names. The resulting file using the above code can be directly used by IIS. Just before switching to the new site I can only regenerate it to include to the new blog posts written in the interim.

Copyright
Creative Commons License