[Controller] and [NonController] attributes in ASP.NET Core MVC

· 527 words · 3 minutes to read

One of the late additions before the RTM release of ASP.NET Core MVC was the introduction of the [Controller] attribute, and its counterpart, [NonController], which were added in RC2.

Together, they allow you to more specifically control which classes should be considered by the framework to be controllers (or controller candidates) and which shouldn’t. They also help you avoid the nasty hacks we needed to do in i.e. ASP.NET Web API to opt out from the “Controller” suffix in the name.

Let’s have a look.

[Controller] vs [NonController] and the built-in conventions 🔗

ASP.NET Core MVC supports the concept of POCO controllers so you no longer need to inherit from the base Controller class in order to create the your HTTP endpoints.

If you choose to use POCO controllers, your class will be considered to be a valid controller simply if it has a Controller suffix in the class name.

So these are the two fundamental prerequisites when authoring MVC controllers:

  • inherit from Controller base class
  • or use a Controller suffix in the name

As a result, the following two controller declarations will work out of the box:

[Route("api/[controller]")]
public class AlphaController : Controller
{
    [HttpGet]
    public string Get()
    {
        return "alpha";
    }
}

[Route("api/[controller]")]
public class BetaController
{
    [HttpGet]
    public string Get()
    {
        return "beta";
    }
}

Now what’s more interesting, is what happens if you define a controller by inheriting from the base Controller, but you omit the Controller suffix in the name?

[Route("api/[controller]")]
public class Foo : Controller
{
    [HttpGet]
    public string Get()
    {
        return "foo";
    }
}

Well, turns out this is also correct. The reason why this can work, is that the base class Controller (or - to be more specific, it’s own base class, called ControllerBase), is decorated with the [Controller] attribute, which indicates that the entire inheritance tree should be considered as valid MVC controllers.

Another possibility is that you’d have a POCO controller, that doesn’t have the Controller suffix in the name. That is shown below:

// wrong
[Route("api/[controller]")]
public class Bar
{
    [HttpGet]
    public string Get()
    {
        return "bar";
    }
}

This will not work out of the box, and this is where you need to use the [Controller] attribute.

[Controller]
[Route("api/[controller]")]
public class Bar
{
    [HttpGet]
    public string Get()
    {
        return "bar";
    }
}

Just like it is the case with the built-in Controller type, you could also decorate a base class with a [Controller] attribute and all the inheriting classes would be considered controllers then.

[Controller]
public class Endpoint {}

[Route("api/[controller]")]
public class Bar : Endpoint
{
    [HttpGet]
    public string Get()
    {
        return "bar";
    }
}

There is also a possibility where you’d have a class, which uses the Controller suffix in the name, but it should not become an MVC POCO controller. That’s where you need to opt out from the controller scan, by applying the [NonController] attribute.

[NonController]
public class DeviceController
{
    // omitted for brevity
}

Finally, what’s also worth mentioning, is that [NonController] will always have higher precendence than [Controller]. In fact, if the [NonController] appears anywhere in the class hierarchy, the type or its descendants will never be considered controller candidates anymore.

About


Hi! I'm Filip W., a software architect from Zürich 🇨🇭. I like Toronto Maple Leafs 🇨🇦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁢󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP