ASP.NET Routing – Generating URLs from Route Data

I just made my first, albeit small, contribution to Stack Overflow is a great programming Q&A site built with ASP.NET MVC. If you haven’t visited, it’s well worth a look. Someone asked a question about how to generate URLs if you’re using ASP.NET Routing with webforms. MVC has nice helper methods on the HTML helper class like Html.ActionLink() that will render a URL given a controller and action method. In webforms though, you’re on your own…

Taking the example I used last week in my Getting ASP.NET Routing Up and Running – The Definitive Guide post and modifying it slightly, imagine you had a route defined like this in global.asax:

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        Dim rte = New Route("{country}/{lang}/articles/{id}/{title}", New RouteHandler())

    End Sub

Of course you could have additional routes defined and at some later point you may modify the route patterns. You need to have a robust way to generate a URL given a particular collection of route parameters. ie you need to be able to ask the question "Given the country is UK, the language is EN-GB, the article id is 777 and the title is Blah blah blah, what URL do I need to make this hyperlink work?". The routing infrastructure can do this for you. You need the GetVirtualPath method on a RouteCollection.

GetVirtualPath(RequestContext, RouteValueDictionary)

The static property RouteTable.Routes is of type RouteCollection so we can call RouteTable.Routes.GetVirtualPath() to get the VirtualPathData (which has a VirtualPath property that will give us the URL). All we need to do is create a RouteValueDictionary of our route data and pass the System.Web.Routing.RequestContext. This gets a little trickier as we don’t, by default, have access to the RequestContext at the point when we’re rendering a response. What we can do is push the RequestContext into the HttpContext in our IRouteHandler. Something like this:

Public Class RouteHandler
    Implements IRouteHandler

    Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) _
        As IHttpHandler Implements IRouteHandler.GetHttpHandler

        requestContext.HttpContext.Items("RequestContext") = requestContext

        Return New ViewHandler() With { _
            .Country = requestContext.RouteData.GetRequiredString("country"), _
            .Id = requestContext.RouteData.GetRequiredString("id"), _
            .Lang = requestContext.RouteData.GetRequiredString("lang"), _
            .Title = requestContext.RouteData.GetRequiredString("title") _

    End Function

End Class

I’m rendering the response with a simple IHttpHandler and I simply set all the route data as properties on my IHttpHandler so I can render this as part of the response (I only have one "view" so this is the only way to see if things are working):

Public Class ViewHandler
    Implements IHttpHandler

#Region "Property Declarations"
    ' You can imagine what goes in here right?
#End Region

    Sub ProcessRequest(ByVal context As HttpContext) _
        Implements IHttpHandler.ProcessRequest

        Dim rvd = New RouteValueDictionary(New With { _
                                            .country = "UK", _
                                            .lang = "EN-GB", _
                                            .id = "777", _
                                            .title = "Blah blah blah"})

        Dim routedurl = _
            RouteTable.Routes.GetVirtualPath(context.Items("RequestContext"), rvd).VirtualPath

        context.Response.ContentType = "text/html"

        Dim htmlString = _
        "<html>" + _
            "<head>" + _
                "<title>Simple View</title>" + _
            "</head>" + _
            "<body>" + _
                "<h1>Simple View</h1>" + _
                "<div>" + _
                "<ul>" + _
                    "<li>" + String.Format("Country: {0}", Me.Country) + "</li>" + _
                    "<li>" + String.Format("Lang: {0}", Me.Lang) + "</li>" + _
                    "<li>" + String.Format("Id: {0}", Me.Id) + "</li>" + _
                    "<li>" + String.Format("Title: {0}", Me.Title) + "</li>" + _
                "</ul>" + _
                    "<a href='" + routedurl + "'>A URL generated from a route</a>" + _
                "</div>" + _
            "</body>" + _

    End Sub

    Public ReadOnly Property IsReusable() As Boolean _
        Implements IHttpHandler.IsReusable
            Return False
        End Get
    End Property

End Class

In my IHttpHandler I want to insert a hyperlink in the response. Notice that I create a new RouteValueDictionary with some route data that I want to turn into the URL for the hyperlink. I then call GetVirtualPath() on the RouteCollection passing in the RequestContext that I’d previously pushed into the HttpContext. From there, the VirtualPath property gives me the URL string I need to render the hyperlink with the correct href.

ASPRoutingNotice in the image of the rendered page to the right (click to see it enlarged):

  1. the original URL data
  2. is reflected in the list items
  3. the URL for the hyperlink is showing in the status bar


Technorati Tags: ,,