views:

324

answers:

1

First off, I'd like to apologise for the ludicrous title. I'm not trying to sound cool or clever by using the word 'bidirectional', I just genuinely couldn't think of another way to describe it. Promise.

On to my problem. I have the following in the <system.webserver>/<rewrite>/<rules> section of my Web.config.

<!-- Who We Are -->
<rule name="1A">
    <match url="^whoweare\.aspx$" />
    <action type="Redirect" url="who-we-are" redirectType="Permanent" />
</rule>
<rule name="1B">
    <match url="^who-we-are$" />
    <action type="Rewrite" url="whoweare.aspx" />
</rule>

<!-- What We Do -->
<rule name="2A">
    <match url="^whatwedo\.aspx$" />
    <action type="Redirect" url="what-we-do" redirectType="Permanent" />
</rule>
<rule name="2B">
    <match url="^what-we-do$" />
    <action type="Rewrite" url="whatwedo.aspx" />
</rule>

Now this works tremendously. Effectively, if you visit the URL http://example.com/whoweare.aspx (which is the actual URL of the page), you'll be 301 redirected to the URL http://example.com/who-we-are (the virtual URL), and if you visit the virtual URL, you'll be rewritten to the actual URL.

This means super sexy URLs without duplication, and it doesn't result in reciprocal rewriting either, so smiles all round.

My question is this: could this be done more elegantly?

It's a little cumbersome having to write out two rules to ensure that one is redirected to the other, and the other is rewritten to the one. Is it possible to write one rule which will achieve the functionality of the above two?

A: 

Elegance is a subjective term, I guess there are a couple of ways that would be better, such as using Rewrite extensibility and implement fancy mapping logic, but by far the way I would recommend is using just 2 rules, one for Redirect and one for Rewrite, and with that just leverage Rewrite Maps that will make it a bit more readable (but still painful) to manage them, for example below you would now only need to maintain the maps and never have to deal with rules anymore:

<system.webServer>
    <rewrite>
        <rules>
            <rule name="Rewrite From Pretty URL" stopProcessing="true">
                <match url=".*" />
                <conditions>
                    <add input="{URLsToRewrite:{REQUEST_URI}}" pattern="(.+)" />
                </conditions>
                <action type="Rewrite" url="{C:1}" appendQueryString="false" />
            </rule>
            <rule name="Redirect To Pretty URL" stopProcessing="true">
                <match url=".*" />
                <conditions>
                    <add input="{URLsToRedirect:{REQUEST_URI}}" pattern="(.+)" />
                </conditions>
                <action type="Redirect" url="{C:1}" appendQueryString="false" />
            </rule>
        </rules>
        <rewriteMaps>
            <rewriteMap name="URLsToRewrite">
                <add key="/who-we-are" value="/whoweare.aspx" />
                <add key="/what-we-do" value="/whatwedo.aspx" />
            </rewriteMap>
            <rewriteMap name="URLsToRedirect">
                <add key="/whoweare.aspx" value="/who-we-are" />
                <add key="/whatwedo.aspx" value="/what-we-do" />
            </rewriteMap>
        </rewriteMaps>
    </rewrite>
</system.webServer>
CarlosAg
Oooh, nice. I'm ashamed to say I wasn't aware of Rewrite Maps. This certainly seems a more maintainable approach. Would you mind, though, if I asked a couple a questions about what's going on in the code you present? Firstly, I'm not quite sure what `<match url=".*" />` is actually matching on, and how it can just match everything? Secondly, will `{REQUEST_URI}` cause problems when working locally, since I may be working within a deeper directory structure than in the release version? Finally, what's the different between `{R:n}` and `{C:n}`? Thanks a bunch Carlos.
David Foster
.* is telling it to match everything in the URL (not including query string) to analyze every request. REQUEST_URI will match the entire URL (including Query string) absolute to the root of the site "/". An easy way to look at this is to enable tracing: http://learn.iis.net/page.aspx/467/using-failed-request-tracing-to-trace-rewrite-rules/Finally {R:#} will return matches within the URL match (in the case above .*), and {C:#} will return matches within the conditions (in this case the result of applying the map.There is a forum for URL Rewrite at http://forums.iis.net/1152.aspx
CarlosAg
Cheers mate. Super helpful and much appreciated.
David Foster