Fluent Controller

The design of the fluent controller is to avoid the Fat controller. We avoid business logic and want it to manage navigation through redirection and rendering. The fluent controller is useful for:
  1. Ensuring user input is valid
  2. Calling a service or repository to perform the unit of work required
  3. Selecting which View or Action should be returned to the user
  4. Test-first design and coding (see the test helpers for testing fluent controllers)

In short, you'll find that there aren't try/catches, exception handling in our controllers!

Here's an example. There are examples in the tests too.

using MvcContrib.FluentController;

public class UserController : AbstractRestfulFluentController
{
     public ActionResult Create(object model)
     {
      return CheckValidCall(() => UserRepository.Create(model))
        .Valid(x => View(RestfulAction.New, x))
        .Invalid(() => RedirectToAction(RestfulAction.Index));
      }
}


The first thing is to inherit from the AbstractRestfulFluentController.

using MvcContrib.FluentController;

public class UserController : AbstractRestfulFluentController
{
    public ActionResult Index()
    {
        return View();
    }
}	


Just by doing this, you'll get access to the Test Helpers. Actually, let's explain the controller functionality through test. Let’s now look at how to create a fleunt (skinny) controller. Let’s take a redirection design:

Test/Design (1)

GivenController.As<UserController>()
    .ShouldRedirectTo(RestfulAction.Index)
    .IfCallSucceeds()
    .WhenCalling(x => x.Create(null));

Controller Action (1)

public ActionResult Create(object model)
{
    return CheckValidCall()
        .Valid(x => RedirectToAction(RestfulAction.Index))
        .Invalid(() => View("New", model));
}


A more complex example of Create is where it calls a repository. Here if the Repository throws an exception it will execute .Invalid otherwise it will execute .Valid.

Test/Design (2)

GivenController.As<UserController>()
    .ShouldRedirectTo(RestfulAction.New)
    .IfCallFails()
    .WhenCalling(x => x.Create(null));

Controller Action (2)

public ActionResult Create(object model)
{
    return CheckValidCall(() => UserRepository.Create(model))
        .Valid(x => View(RestfulAction.New, x))
        .Invalid(() => RedirectToAction(RestfulAction.Index));
}


So far you have seen two fluent controller actions available, however, also available are:

* InvalidNoNewErrors
* Other

And within the context of CheckValidCall you also get access:

* controller
* model
* isValid
* newErrors

Another feature to be aware of is that you can create own extensions. The current CheckValidCall returns a FluentControllerAction<T> so all you have to do is create an extension on this as you would with an extension method. You can even rewrite the CheckValidCall and create your own one.

Last edited Mar 10, 2010 at 5:57 AM by toddb, version 2

Comments

ibsukru Apr 3, 2012 at 3:54 PM 
Great effort. But keep in mind that redirection is not a part of RESTful apis. http://stackoverflow.com/questions/9648981/is-it-ok-to-use-http-redirects-in-a-restful-mvc-framework