Handling Legacy Urls in Asp.Net MVC 3

by StefanOlson 26. April 2011 15:57

Update: I have discovered that the way this was originally written by Matt Hawley will cause asp.net MVC to incorrectly generate route urls. This is fixed by overriding GetVirtualPath. Code has been updated below.

I'm currently in the process of transforming the PalaceVirtualTours website from asp.net web forms to asp.net MVC 3. 

However there are a number of pages that already have URLs that are linked to by other sites or have reasonable search engine rankings, so if someone tries to go to that page it needs to be redirected to the the new MVC URL.

Matt Hawley wrote an article about this sometime ago: http://www.eworldui.net/blog/post/2008/04/ASPNET-MVC---Legacy-Url-Routing.aspx

Unfortunately the code that he provides doesn't work, MVC reports that a controller is required, as described in this stack overflow question: http://stackoverflow.com/questions/2528078/legacy-url-rewriting-with-query-string-parameters

The solution to this is to use an IHttpHandler directly, rather than an MVCHandler.

Here is the full updated, working code:

// The legacy route class that exposes a RedirectActionName
public class LegacyRoute : Route
{
    public LegacyRoute(string url, string redirectRuleName):base(url, new LegacyRouteHandler())
    {
        RedirectRuleName = redirectRuleName;
    }

    public string RedirectRuleName { get; set; }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        return null;
    }
}

// The legacy route handler, used for getting the HttpHandler for the request
public class LegacyRouteHandler : IRouteHandler
{
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new LegacyHandler();
    }
}

// The legacy HttpHandler that handles the request
public class LegacyHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var requestContext = context.Request.RequestContext;
        string redirectActionName = ((LegacyRoute)requestContext.RouteData.Route).RedirectRuleName;

        var queryString = requestContext.HttpContext.Request.QueryString;
        foreach (var key in queryString.AllKeys)
        {
            requestContext.RouteData.Values.Add(key, queryString[key]);
        }

        VirtualPathData data = RouteTable.Routes.GetVirtualPath(requestContext, redirectActionName, requestContext.RouteData.Values);

        context.Response.Status = "301 Moved Permanently";
        context.Response.AppendHeader("Location", data.VirtualPath);
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

And here is how to use it:

routes.MapRoute(
    "BPTour", // Route name
    "buckingham-palace-virtual-tour", // URL with parameters
    new { controller = "Tours", action = "BuckinghamPalace", id = UrlParameter.Optional } // Parameter defaults
);
// redirect /bptour to the BPTour route.
routes.Add("", new LegacyRoute("bptour", "BPTour"));

Tags:

asp.net MVC

Introduction to JQuery Ui Server Controls

by StefanOlson 25. April 2011 15:20

Today I have released onto codeplex a new library, the jQuery UI server controls:

http://jqueryuiserverctls.codeplex.com/

This library is intended to make sure that when you use jQuery controls such as tabs you don't end up with the page rendering without the jQuery styling applied.  This happens because the scripts to create things such as tabs doesn't execute until after the page is loaded. This is jarring to the user because they see the tabs appearing like this:
image
Before the script gets applied.

And then it appears like this:
image

Whilst you may not notice this when running on localhost, a site like this demonstrates it adequately:
http://en.webdiyer.com/MvcPager/Demo/MsAjaxOrders

If you use this library to create your tabs all that styling is applied on the server and thus your page always appears the way it should when the client first sees it!

How to use it

Normally, if you were using jQuery UI to create the tabs above , here is how it would look:

<script type="text/javascript">
    $(function () { $("#nonservertabs").tabs(); });
</script>         

<div id="nonservertabs">
    <ul>
        <li><a href="#nonservertabs-1">Gallery</a></li>
        <li><a href="#nonservertabs-2">List</a></li>
    </ul>
    <div id="nonservertabs-1">
    This is the gallery tab
    </div>
    <div id="nonservertabs-2">
    <p>This is the list tab at @DateTime.Now.ToString()</p>
    </div>
</div>

Here is how the same code can be created using the  jQuery UI server controls:

@Html.JQueryUiTabs(new JQueryUiTabsSettings
{
    Items = new List<JQueryUiTab>
                {new JQueryUiTab{Title="Gallery"}.SetContent(@<text>This is the gallery tab</text>),
                 new JQueryUiTab{Title="List"}.SetContent(@<p>This is the list tab at @DateTime.Now.ToString()</p>)}
}).Render()

Which results in this output:

<script type="text/javascript">$(function(){$('#tabs').tabs({selected:0,});});
</script>
<div class="ui-tabs ui-widget ui-widget-content ui-corner-all" id="tabs">
<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">
<li class="ui-state-default ui-corner-topui-tabs-selected ui-state-active"><a href="#tabs-1">
<
span>Gallery</span></a></li><li class="ui-state-default ui-corner-top"><a href="#tabs-2">
<
span>List</span></a></li></ul> <div class="ui-tabs-panel ui-widget-content ui-corner-bottom" id="tabs-1"> This is the gallery tab </div> <div class="ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" id="tabs-2"> <p>This is the list tab at 25/04/2011 5:28:51 p.m.</p> </div> </diV>

As you can see, all the styles required by jQuery are automatically inserted for you, which ensures that the client sees the tabs as you intended without any potential delay caused by script execution.

There are other options  you can apply, such as cookies that I will talk about the future blog posts.

Download the library for yourself today and try it out! Would love to get your feedback on how it can be improved.

Tags:

asp.net MVC | jQuery Ui Server ControlsUi Server Controls

Silverlight 5: does anyone at Microsoft talk to each other?

by StefanOlson 25. April 2011 12:11

The Silverlight 5 beta was made available a couple of weeks ago (http://www.silverlight.net/getstarted/silverlight-5-beta/).  Most of the features had been telegraphed in December at the Microsoft Silverlight firestarter which appeared to have been hastily organised to try and fend off criticism that Silverlight was being deprecated in favour of HTML 5.

Certainly there was, and still is some confusion as to what the purpose of Silverlight is. It was originally sold to us as a cross-platform browser independent platform.  That now seems to have changed and it is HTML 5 is the cross-platform browser independent platform.  Unfortunately the tooling to support developing HTML and JavaScript is absolutely appalling in comparison with the outstanding tools for Silverlight.

Based on this, a website that I was developing for a customer was going to have all the public parts written in HTML and the pieces where you need to login written in Silverlight.  I have now changed that and completely removed Silverlight from the project and it will all be in HTML.

So why would anyone use Silverlight? This is a good question, if you want to have a rich application that can make use of the available hardware, why wouldn’t you build a WPF application?  Well partially because WPF appears to be completely on the scrapheap. The WPF Toolkit hasn't even been updated to support visual studio 2010 and the .net 4 framework!

My previous post on LightSwitch described how they had chosen to use Silverlight rather than WPF for desktop applications.  This appears to be what Microsoft wants you to do, with continued improvements in full trust and multi-window support.

So what's new in Silverlight 5 that isn't available in WPF:

  • XAML Debugging with breakpoints for binding debugging. This looks outstanding!
  • Double (and multi) click support
  • GPU-accelerated XNA-compatible 3D and immediate-mode 2D API
  • RichTextBox overflow

But there has been no word on when or if any of these features will be available in WPF. Why is there not a new release of the WPF Toolkit with the 3-D and 2-D API available? Those of us who have invested massive amount of time in developing 3-D software using WPF (in my case for the 360° panoramas in the virtual tours) have to start from scratch, yet again!  Whilst it may be a good move to use XNA, as it is available on multiple platforms, it means that people working in both WPF in Silverlight have two different Apis to write for.

The RichTextBox overflow is completely baffling to me, why did they just not port flow documents?

There are also plenty of features already in Silverlight that are not available in WPF:

  • DeepZoom
  • Ria services - to use this you have to create a WCF service and you don't get all the advantages that you do when working with it in Silverlight
  • HyperlinkButton
  • ChildWindow
  • UriMapping

It appears from some rumours that Silverlight will be the primary programming language for Windows 8, once again suggesting that WPF has a limited future, which may be why these features have not made it into WPF.

What’s still missing from in Silverlight from WPF:

  • MultiBinding (see here for an alternative)
  • Preview events (in WPF all events such as mouse button down have a preview event that runs down the visual tree)
  • Triggers
  • FlowDocuments (see here for my attempt at FlowDocuments)
  • RoutedEvents
  • BooleanToVisibilityConverter
  • GroupBox
  • Toolbars
  • Expander
  • Commanding infrastructure
  • Default set of brushes
  • Many ToolTip options
  • I'm sure there are more than I have missed.

All of these make developing Silverlight a lot harder and you often find yourself hacking around the lack of these features.  I have built a library to try and ease this, but you are forced to have separate xaml between the two targets because there are so many things not available in both.

To top this off, there is a new release of Silverlight for the Windows phone, but that is only Silverlight 4!

If, like me you are building an application for WPF, Silverlight and Windows Phone, this just makes life incredibly difficult because across all three platforms there are features that are available in completely different ways! 

One wonders why people at Microsoft don't sit down and talk to each other and try and make these things simpler for developers, especially since some developers are likely to abandon WPF because of the complexity of trying to manage Silverlight and WPF development, or developers like me will abandon Silverlight development for public facing websites.

There are certainly some good new features in Silverlight 5, but I hope that the various teams at Microsoft will start talking to each other and create a more cohesive development platform.  With Google already way ahead on the phone market and wanting to get into the operating system market Microsoft really have to up their game.

Tags:

Silverlight | WPF

About the author

Stefan Olson is the Managing Director of Olson Software.  He has been developing software using Microsoft Technologies for nearly 20 years.

He is currently working on building the next generation Virtual Tour software in WPF and Silverlight for www.palacevirtualtours.com.