C
C#3y ago
Anton

Combining attributes ASP.NET Core

Say I want to apply the following attributes to all of my API controllers:
[ApiController]
[Route("api/[controller]")]
[Produces(MediaTypeNames.Application.Json)]
[Consumes(MediaTypeNames.Application.Json)]
[ApiController]
[Route("api/[controller]")]
[Produces(MediaTypeNames.Application.Json)]
[Consumes(MediaTypeNames.Application.Json)]
Now I know there's no way in pure C# to combine multiple attributes into one, or have a function or a class that returns or represents multiple attributes. I also know that the only way to kinda combine them in pure C# would be to make a base class and inherit from it:
[ApiController]
[Route("api/[controller]")]
[Produces(MediaTypeNames.Application.Json)]
[Consumes(MediaTypeNames.Application.Json)]
public abstract class ApiControllerBase : Controller
{
}
[ApiController]
[Route("api/[controller]")]
[Produces(MediaTypeNames.Application.Json)]
[Consumes(MediaTypeNames.Application.Json)]
public abstract class ApiControllerBase : Controller
{
}
which is bad for obvious reasons. Is there maybe some IControllerMetadataProvider sorta thing in ASP.NET Core that could effectively allow me to combine the attributes, or is inheritance / repetition the only way? Is there maybe a way to add this default configuration to all controllers from a certain assembly? enlighten me, I'm new to the framework.
3 Replies
Anton
AntonOP3y ago
So I just read this https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-6.0#modify-the-controllermodel-description and made this
public class Boilerplate : Attribute, IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
if (controller.Filters.All(f => f is not IResourceFilter))
controller.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json));

if (controller.Filters.All(f => f is not IResultFilter))
controller.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json));

if (controller.Selectors.All(s => s.AttributeRouteModel is null))
{
controller.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Name = controller.ControllerName,
Template = "api/[controller]",
}
});
}
}
}
public class Boilerplate : Attribute, IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
if (controller.Filters.All(f => f is not IResourceFilter))
controller.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json));

if (controller.Filters.All(f => f is not IResultFilter))
controller.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json));

if (controller.Selectors.All(s => s.AttributeRouteModel is null))
{
controller.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Name = controller.ControllerName,
Template = "api/[controller]",
}
});
}
}
}
which surprisingly totally works. I have no idea what I'm doing tho. However, it doesn
Work with the application model in ASP.NET Core
Learn how to read and manipulate the application model to modify how MVC elements behave in ASP.NET Core.
Anton
AntonOP3y ago
it doesn't work with ApiController It seems i gets resolved before all that, and I don't even know if what I'm doing is adequate at all So first, is there a way to apply ApiController from within that IControllerModelConvention, and secondly, is what I'm doing even reasonable? Ok so [ApiController] requires at least one IRouteTemplateProvider attribute be present, and the api behavior is actually enabled by IApiBehaviorMetadata, so I combined these into one to both enable the default route and enable api conventions, which works. No base classes, no boilerplate.
public class ApiControllerConventionAttribute : Attribute,
IControllerModelConvention,
IApiBehaviorMetadata,
IRouteTemplateProvider
{
public void Apply(ControllerModel controller)
{
if (controller.Filters.All(f => f is not IResourceFilter))
controller.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json));

if (controller.Filters.All(f => f is not IResultFilter))
controller.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json));

Name ??= controller.ControllerName;
}

public string? Template { get; set; } = "api/[controller]";
public int? Order { get; set; }
public string? Name { get; set; }
}

[ApiControllerConvention]
public class StuffController : Controller
{
// ...
}
public class ApiControllerConventionAttribute : Attribute,
IControllerModelConvention,
IApiBehaviorMetadata,
IRouteTemplateProvider
{
public void Apply(ControllerModel controller)
{
if (controller.Filters.All(f => f is not IResourceFilter))
controller.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json));

if (controller.Filters.All(f => f is not IResultFilter))
controller.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json));

Name ??= controller.ControllerName;
}

public string? Template { get; set; } = "api/[controller]";
public int? Order { get; set; }
public string? Name { get; set; }
}

[ApiControllerConvention]
public class StuffController : Controller
{
// ...
}

Did you find this page helpful?