How I permanently redirect using fubuMVC

In the process of migrating to fubuMVC I’ve previously touched upon a change in urls in one of my previous posts on my experiences with fubuMVC.
In this process I have changed the urls for my static pages from “/home/news” to “/news” for instance and this means that I now have to deal with permanent redirections and issuing 301 redirects for the original urls.

With fubuMVC, I’m going to achieve this with a custom behaviour that is inserted into the behaviour chain.

(to learn more about behaviour chains there are a number of good articles out there, here’s one:
http://murrayon.net/2011/06/fubumvc-behavior-chains-bmvc-pattern.html
)

In order to write a custom behaviour that issues a response I am going to need to interact with the IOutputWriter abstraction and write a response code and custom headers to show the new location of the permanent redirect.

At the time of writing this, the IOutputWriter doesn’t yet support writing headers so I’m going to have to extend its functionality for the time being. Here’s the current interface in fubuMVC:

  public interface IOutputWriter
  {
    void WriteFile(string contentType, string localFilePath, string displayName);

    void Write(string contentType, string renderedOutput);

    void RedirectToUrl(string url);

    void AppendCookie(HttpCookie cookie);

    void WriteResponseCode(HttpStatusCode status);

    RecordedOutput Record(Action action);
  }

So here’s a simple extension which will write my header directly into the HttpResponse (normally this would be in the HttpOutputWriter concrete class, but I’m extending the interface for my own project here, so don’t have much choice but to place it there; perhaps we can get it added to fubuMVC very soon).

  public static class OutputWriterExtensions
  {
        public static void WriteHeader(this IOutputWriter outputWriter, 
                        string headerName, string headerValue)
        {
            HttpContext.Current.Response.AppendHeader(headerName, headerValue);
        }
  }

I’m also going to wrap the writing of the response code and the header generation into a specific method, again an extension. I also need to send the IEndpointService in the method call because it is an extension method.
Here’s the wrapper:

    public static class OutputWriterExtensions
    {
        public static void WritePermanentRedirectTo<T>(this IOutputWriter outputwriter, 
                Expression<Action<T>> actionExpression, IEndpointService endpointService)
        {
            var endpoint = endpointService.EndpointFor(actionExpression);
            outputwriter.WriteHeader("Location", endpoint.Url);
            outputwriter.WriteResponseCode(HttpStatusCode.MovedPermanently);
        }
    }

This now enables me to point at a method on an action and permanently redirect to it.

Now for the behaviour itself – I’m going to inherit from BasicBehavior which does most of the heavy lifting for me leaving me to override the perform invoke method:

    public class PermanentNewsRedirectionBehaviour : BasicBehavior
    {
        private readonly IOutputWriter _outputWriter;
        private readonly IEndpointService _endpointService;

        public PermanentNewsRedirectionBehaviour(IOutputWriter outputWriter, 
                                                 IEndpointService endpointService) 
          : base(PartialBehavior.Ignored)
        {
            _outputWriter = outputWriter;
            _endpointService = endpointService;
        }

        protected override DoNext performInvoke()
        {
            _outputWriter.WritePermanentRedirectTo<NewsController>(c => c.Root(), 
                                                                   _endpointService);
            return DoNext.Stop;
        }
    }


This is a behaviour that will redirect to the news action using the new extension method I’ve written for the output writer.
The simplest way of putting this into my fubuMVC registry is to use the “EnrichCallsWith” functionality for policies:

    public class XercesFubuRegistry : FubuRegistry
    {
        public XercesFubuRegistry()
        {
            IncludeDiagnostics(true);

            Applies.ToThisAssembly();

            Actions.IncludeClassesSuffixedWithController();

            Routes.HomeIs<HomeController>(c => c.FrontPage())
                    .IgnoreControllerNamespaceEntirely()
                    .UrlPolicy<EnsureControllersCanHaveARootUrlPolicy>();

            Policies.EnrichCallsWith<PermanentNewsRedirectionBehaviour>(call => 
                call.HandlerType == typeof(HomeController) &&
                call.Method.Name.ToLower() == "news");

            this.UseSpark();

            Views.TryToAttachWithDefaultConventions();
        }
    }

The new part of the registry code above is the Policies section where I am enriching a call to the HomeController for a “news” method, a new one as a placeholder for this redirection:

    public class HomeController
    {
        public HomeViewModel FrontPage()
        {
            return new HomeViewModel();
        }

        public AutomaticRedirection News()
        {
            return new AutomaticRedirection();
        }
    }

    public class HomeViewModel
    {}

    public class AutomaticRedirection
    {}

I’ve created a new class called AutomaticRedirection just as another placeholder to indicate what will happen for this method.
In order to see this new functionality I can look at another part of the fubuMVC diagnostics – the behaviour chain and the specific chain for the “home/news” route:

redirectchain 

I now have permanent redirection in place and baked into fubuMVC in the way that I want it to.
By enriching this action, the action call will occur first and then the wrapper takes effect and permanently redirects - all requests to “home/news” now 301 redirect to “news”.

In the next post I will show how a custom convention that implements IConfigurationAction can instead give me more fine grained control over where the behaviour is inserted in the graph.



0 comments: