ASP.NET MVC – One Route, Many Actions

      No Comments on ASP.NET MVC – One Route, Many Actions

I’ve been spending a lot of time looking at OAuth2 recently, both from a provider and consumer point of view.

As a provider, one of the first things I saw as an architectural issue was the concept of different grant types (authorization_code, implicit etc.) being treated as a parameter within a single OAuth2 framework. The validation rules, the logic being invoked, there’s a commonality in terms of contract but the fact is the specifics and the internals of each grant type are significantly different.

My requirements couldn’t be better mapped out than The OAuth2 RFC so I knew what my end results had to be. But from a coding point of view, how to ensure this functionality is testable and isolated posed an interesting question. Because there are a lot of moving parts and even the cleanest re-factoring would be costly, my plan was to write one of the grant types with stubbed objects and methods just to get the flow right – then go back and add in the detail if I was happy it was maintainable and extensible for the others.

My first thought was a take on the strategy pattern; if my validation rules and business logic were held in a collection of objects then I could inject a factory which returned the right piece based on the grant_type parameter of the request.

This initially worked well, each object testable and the controller was none the wiser about the functionality. Good job! The issue came when I looked at extending it.

Each OAuth2 grant type has the same core input parameters, but each have parameters specific to only a subset of types. The issue with extending this strategy pattern was that my input had to extend to allow all combinations from all types. I could customise the model binding for the input parameters, but even so all my validation logic would involve type checking.

What this all boiled down to was that the approach was busted. Rather than isolation and maintenance, the customisation of each part of the process was now showing the fact that unless it was handled really early on, all my code would be nothing more than patterns hiding this need for a big “if…else”.

So the only way to stop making this decision in each layer was to have the decision made earlier on, and as my issue started with correct request input – that meant altering the way MVC routed my code.

Enter ActionMethodSelectorAttribute

I knew that my code would want to fork its process depending on the grant_type querystring parameter, so what I did was write the following code so that MVC would dismiss an action routed to this unless the grant_type matched the one assigned to that instance of the attribute.

public class GrantTypeFilterAttribute: ActionMethodSelectorAttribute
    {
        private string GrantType { get; }

        public GrantTypeFilterAttribute(string grantType)
        {
            GrantType = grantType;
        }

        public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
        {
            var query = routeContext.HttpContext.Request.Query;
            return query.ContainsKey("grant_type") && query["grant_type"] == GrantType;
        }
    }

This meant that I could make MVC routing do all the heavy lifting, and each flow could use objects strongly typed to its own requirements.

[HttpGet, GrantTypeFilter("authorization_code")]
public ActionResult RequestToken(AuthorizationCodeTokenRequest request){}

[HttpGet, GrantTypeFilter("implicit")]
public ActionResult RequestToken(ImplicitTokenRequest request){}

This also meant that extensibility could be achieved with even less work, although my code shows two actions next to each other, in reality they’re each in their own controller bound to the same routes and using the same attribute. This is far more isolated and easier to maintain than pushing each flow into a set of common patterns.

I think as a .NET developer you get used to the way MVC works by default, and with DI frameworks able to make more and more decisions and apply more logic you get used to the process of boiling down your specific requirements into an extensible object model so they fit within your action signature.

But if your requirement is actually that you customisation and just to give the premise of a single flow to your user? Making a small tweak like this one can be hugely powerful.

Leave a Reply

Your email address will not be published. Required fields are marked *