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.