Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single Responsibility Principle in MVC controllers. Critique required

In my MVC4 app, there are some actions that need to behave differently depending on whether you are logged in (FormsAuthentication in my case) or not.

For example, I have an AccountController that has a method "RenderAccountAndProfile". If logged out, the corresponding partial view displays a log in prompt and button. If a user is logged in, the user's profile links appear, alongside a log out button.

The approach I've taken thus far in projects is to simply have an if statement along the lines...

        if (HttpContext.User.Identity.IsAuthenticated)
        {
            ...
        }
        else
        {
            ...
        }

However, I've just created what I think is a reasonably elegant alternative to this approach.

I have created a new attribute called AnonymousUsersOnly, which is very simple:

public class AnonymousUsersOnlyAttribute : System.Web.Mvc.ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(System.Web.Mvc.ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return !controllerContext.HttpContext.User.Identity.IsAuthenticated;
    }
}

My AccountController class is decorated with the Authorize attribute. This then enabled me to have the following code:

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    [AnonymousUsersOnly]
    [ActionName("RenderAccountAndProfile")]
    public ActionResult RenderAccountAndProfile_Anonymous()
    {
        // create a "logged out" view model
        return Content("**NOT LOGGED IN** - LOG IN HERE");
    }

    [ActionName("RenderAccountAndProfile")]
    public ActionResult RenderAccountAndProfile_Authorized()
    {
        // create a "logged in" view model
        return Content("**LOGGED IN** - LOG OUT");
    }
}

I quite like this approach because my action methods conform to the Single Responsibility Principle. Each method now only deals with either a logged in situation or a logged out situation. I don't need any "if" statements directing the traffic any more.

This ought to make Unit Testing easier too, since each method is now only concerned with one outcome, not two. We can write unit tests to test each outcome separately, calling different methods.

Clearly, I can't have two methods with the same signature so that's why I have to use the ActionName attribute.

I would appreciate your critique here. Do you think this is an elegant solution or not? What are the pros and cons to this approach? And what security implications/risks could there be with this?

like image 768
SimonGoldstone Avatar asked Feb 04 '26 22:02

SimonGoldstone


1 Answers

The problem you have here is a strategy pattern problem. And you've implemented a (non-standard) strategy pattern, with a pretty clever implementation. I worry that it is too clever. That cleverness makes what the code is doing a lot less obvious to the uninitiated.

Offhand, I'd favor not bothering. I often write controllers as very thin adapters over domain objects/services. Therefore, I'm willing to take a pragmatic approach to design perfection in the controller. In deciding between slight design problems and obvious code, always choose obvious code.

If you have thicker controllers, or some other reason to be really concerned about this problem here, you might consider a more traditional strategy pattern, perhaps aided by an abstract factory that provides different strategy implementations based on authentication state. This meets your design objectives, and will be more immediately familiar to other programmers (if they know design patterns).

All that being said, I don't think keeping your clever solution will hurt anything that much. I'd be tempted to change the name; Deny seems like a weird verb there to me. Perhaps AnonymousUsersOnly, that would be a little more communicative to future programmers.

like image 164
tallseth Avatar answered Feb 06 '26 12:02

tallseth



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!