[ASP.NET Core MVC Pipeline] Controller Initialization – Action Selection

So, we just finished looking at the Routing Middleware, and that also completes our walk-through the Middleware Pipeline! What happens next? Now we enter the realm of the Controller Initialization, and the first thing we need to do is the Action Selection. Let’s revisit our MVC Core Pipeline flow.

The ASP.NET Core MVC Pipeline
The ASP.NET Core MVC Pipeline

We will now focus on the green part of our pipeline, the Controller Initialization.

Controller Initialization
Controller Initialization

The objective of the process in the green box is:

  1. Find the most suitable Action in the application for that request
  2. Call the Controller Factory informing the required Action
  3. Get an instance of a Controller from the Controller Factory

That is all it does, and it is a very important job 🙂 But, how can we interfere with this process? The first thing we can do is add some rules to make the Action Selection behave as we want it to.

The easiest way

You don't have to customize everything
You don’t have to customize everything

It’s true! You probably already used some alternatives to bend the process of Action Selection towards your objectives. The most common method is to use the Verb Attributes. Let’s imagine you want to create 4 actions in a single controller and each of them will respond to a different HTTP Verb:

What is wrong with this code? All the methods claim the same action path on the route, and the Action Selector has no good way to define which one to call! What will happen when we try to access the /index route?

Ambiguous Exception
Ambiguous Exception

Ambiguous Exception! and that happens, as you can see in the underscored line, because the framework does not have enough information to decide which one is the best candidate action, and that is where we can use a MethodSelectorAttribute:

Now the framework will know the best action to choose based on the HTTP Verb of the Request 🙂

That code exemplifies the kind of intervention that we can do in the process of choosing the most fitting Action Method. But, what if we want to change this behavior in a way that is specific to some kind of logic that we envisioned? That is when you should think about adding an Action Constraint.

What is an Action Constraint?

An Action Constraint is a way that we have to tell the Action Selection process that some method should be a better candidate than the other options for that request. It is really that simple. An action constraint is a class that implements the following Interface:

The Order property will help you define the priority in which that constraint must be evaluated and the Accept method is where the true logic is implemented. Whenever an ActionConstraint is evaluated and the return of the Accept method is TRUE, then it will tell the Action Selection process that this Action is a better suitable match for the request.

Customizing the Action Selection – Custom Action Constraint

Now let’s implement our own IActionConstraint and force the Action Selection process to work as we want it to. Let’s imagine a scenario where we want to serve specific content to our users who access our application through a Mobile Browser, and we want to handle that on the back-end, as we really will serve different data to this users. In this situation we have the following Action Methods:

That would, again, give us the AmbiguousException because, as it is, is impossible to the framework to choose a better Action between those two, so what can we do to help? Let’s implement our action constraint:

I know, I know… there are sure better ways to implement this behavior and that not fool-proof at all, but it is enough for our intents. We set the Order property of our Action Constraint to “0” so it will be one of the first to be evaluated by the framework, and the implementation of our Accept Method returns true if the request’s user-agent container either “Android” or “iPhone” in its value.

So, how to hookup this component to our pipeline? Easy enough:

Ha! Simple, isn’t it?

Results

Default Content
Default Content

When accessing through a common browser, you are going to be redirected to the default implementation of our View…

Mobile Content
Mobile Content

…and when accessed through a Mobile Browser, you will be presented with the specific implementation of our View. Cool, right?

This is one of my favorite pluggable components in the entire framework pipeline. It doesn’t feel too much invasive, and it can help us bend the flow of the request in a very useful way!

What do you think of it? Can we think of a way to use it on your applications?

Source:

Leave a Reply