My name is Rex Morgan and I'm a Staff Software Engineer in Austin, TX working at Indeed.

I love learning new languages and tinkering with new technologies. I'm currently having fun with Elixir, Java, C#, and Kubernetes.

FubuMVC: Redirect and Transfer

Sometimes instead of simply rendering a view from your action, I need to redirect the user to another URL, or call another action. In a multi-tenant application I'm working on, I handle all default requests to many domains with a single action in my FubuMVC application. I want to choose which behavior chain to run, depending on the URL that the user has browsed to. In this post I'm going to explain how FubuMVC allows you to perform these actions.

An example of this, is Tumblr. Browsing to tumblr.com should display the home page for Tumblr (sign up form, information about why Tumblr is great, etc). However, browsing to {username}.tumblr.com should display the posts by that particular user. I honestly don't know if this is handled by a single piece of code on Tumblr, but I'd like to mimic this, with a single piece of code, in my project.

To start, I'm going to create a FrontLoaderEnpoint that all default requests will go through.

In my FrontLoaderEndpoint, I need to check what URL the user is coming from. In order to do this, I have a property called HTTP_HOST on my input model. FubuMVC will automatically fill in the information from the ServerVariables collection. I hate to just leave it as this "just works", but model binding will be the topic of another blog post.

Now I'm taking the HTTP_HOST from the input model and attempting to look up the user that the domain belongs to.

If there is a user that is using the domain, then I'm going to go ahead and transfer the request to the endpoint that handles the user specific domains. I tell FubuMVC that I want to transfer the user by returning FubuContinuation.TransferTo. The TransferTo method takes in the input model of the action that you want to call.

If there's not a user found for the domain, I'm going to redirect the user to the home page (which allows new users to sign up, etc). To redirect the user I return FubuContinuation.RedirectTo and, just like the TransferTo method, pass in the input model of the action I want to redirect the user to.

public class FrontLoaderRequestModel
{
    public string HTTP_HOST { get; set; }
}

public class FrontLoaderEndpoint
{
    private readonly IUserRepository _userRepository;
    
    public FrontLoaderEndpoint(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public FubuContinuation Get(FrontLoaderRequestModel input)
    {
        var user = _userRepository.GetByDomain(input.HTTP_HOST);
        
        if(user != null)
        {
            var userSpecificRequestModel = new UserSpecificIndexRequestModel
            {
                User = user
            };
            return FubuContinuation.TransferTo(userSpecificRequestModel);
        }

        return FubuContinuation.RedirectTo(new HomeRequestModel());
    }
}

How does it work?

The way this works is RedirectTo and TransferTo are both static methods on the FubuContinuation class, which return a FubuContinuation object. In the constructor of the FubuContinuation object, it takes in the ContinuationType (Redirect, Transfer, or NextBehavior) and an action that should be called on the IContinuationDirector.

The IContinuationDirector is what actually does all of the work, by calling RedirectToUrl on the IOutputWriter if you're doing a redirect (just like I did in Step 2 of FubuMVC: Authentication.), or building a partial from the IPartialFactory and invoking it, if you're doing a transfer. The action to call is resolved using the input model that you provided when you called FubuContinuation.RedirectTo / FubuContinuation.TransferTo. Since we're using the thunderdome pattern, each of our actions are uniquely defined by the type of the input model.

Internally, FubuMVC has a built-in convention which gets run. This convention checks for any actions that have a FubuContinuation as an output type. If it finds one, it adds an internal behavior called ContinuationHandler directly after the action call.

When the ContinuationHandler gets called, it pulls the FubuContinuation that was set in my action call from the IFubuRequest and calls Process on it, passing into it, this (the ContinuationHandler being called.) The Process method calls the action that was passed into the FubuContinuation constructor and passes in the ContinuationHandler that's being run. (Note: ContinuationHandler implements IContinuationDirector.)

Bonus!

If you want to redirect the user to a URL of an outside site, just pass the URL directly into FubuContinuation.RedirectTo. The ContinuationHandler has a mechanism in place to redirect directly to a URL if it's provided as a string.