Vinicius Andrade
Vinicius Andrade

Reputation: 586

Bind custom attribute value to route constraint

How do I bind a custom attribute value to a route?

What I want:

[ApiController]
[CustomAttribute("value")]
[Route("{customAttributeValue}/[controller")]
public class ExampleController : Controller
{
    //...
}

Then my route should appear like this. enter image description here

I saw that for the first time when versioning apis

[ApiController]
[ApiVersion("2")] 
[Route("v{version:apiVersion}/[controller]")]
public class ExampleController : Controller
{
    //...
}

So in swagger, this example controller looks like this: enter image description here

Upvotes: 5

Views: 780

Answers (2)

pfx
pfx

Reputation: 23369

Start by defining that custom attribute, which accepts the string value that must be part of the route.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class CustomValueAttribute : Attribute
{
    public CustomValueAttribute(string value)
        => Value = value;

    public string Value { get; }
}

Set up an IActionModelConvention to apply that attribute value to the route of the controller being executed. Here you decide upon the name you want to apply as a placeholder in the route definition; the below example uses customValue.

public sealed class CustomValueRoutingConvention : IActionModelConvention
{
    public void Apply(ActionModel action)
    {
        var value = action.Controller.ControllerType.GetCustomAttribute<CustomValueAttribute>()?.Value;
        if (value is not null)
        {
            action.RouteValues.Add("customValue", value);
        }
    }
}    

Next step is to hook the above convention to the MvcOptions.
You can do that when calling AddControllers.

builder.Services.AddControllers(
    mvcOptions => mvcOptions.Conventions.Add(new CustomValueRoutingConvention())
);

Finally, that CustomValueAttributeand route placeholder get applied to the controller. Notice the [customValue] placeholder in the route.

[ApiController]
[CustomValue("value")]
[Route("[customValue]/[controller]")]
public class ExampleController : ControllerBase

A view from Swagger

enter image description here

Upvotes: 4

Geoffrey Fook
Geoffrey Fook

Reputation: 716

You could use a custom route attribute to solve this.

Example code:

namespace Test.Controllers
{

public class MyCustomValueAttribute : RouteAttribute
{

    public MyCustomValueAttribute(string route, string prefixRoute, int prefixRouteCustomValue) : base($"{prefixRoute}/{prefixRouteCustomValue}/{route}")
    {


    }
}


[ApiController]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    
    [HttpGet]
    [MyCustomValue("[controller]", "value", 20)]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

}

Outputs: enter image description here

Upvotes: 1

Related Questions