ASP.NET Core API:添加自定义路由令牌解析器

Мак*_*вой 3 c# asp.net-core asp.net-core-webapi asp.net-core-routing

我在我的项目中使用https://github.com/ardalis/ApiEndpoints(每个控制器一个操作),但我遇到了[Route("[controller]")]不太适合我的问题,因为控制器如下所示:

图像

我需要类似的东西[Route("[namespace]")],但 ASP.NET Core 不支持它。

有没有办法在 中添加自定义路由令牌解析Startup.cs

到目前为止我的解决方案

  • 硬编码路线
  • 创建包含带有自定义令牌的路由的自定义属性,以及将解析自定义令牌并生成Route属性的源生成器。

Мак*_*вой 5

非常感谢@Kahbazi 为我指明了正确的方向!

这是我想出的:

private class CustomRouteToken : IApplicationModelConvention
{
    private readonly string _tokenRegex;
    private readonly Func<ControllerModel, string?> _valueGenerator;

    public CustomRouteToken(string tokenName, Func<ControllerModel, string?> valueGenerator)
    {
        _tokenRegex = $@"(\[{tokenName}])(?<!\[\1(?=]))";
        _valueGenerator = valueGenerator;
    }

    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            string? tokenValue = _valueGenerator(controller);
            UpdateSelectors(controller.Selectors, tokenValue);
            UpdateSelectors(controller.Actions.SelectMany(a => a.Selectors), tokenValue);
        }
    }

    private void UpdateSelectors(IEnumerable<SelectorModel> selectors, string? tokenValue)
    {
        foreach (var selector in selectors.Where(s => s.AttributeRouteModel != null))
        {
            selector.AttributeRouteModel.Template = InsertTokenValue(selector.AttributeRouteModel.Template, tokenValue);
            selector.AttributeRouteModel.Name = InsertTokenValue(selector.AttributeRouteModel.Name, tokenValue);
        }
    }

    private string? InsertTokenValue(string? template, string? tokenValue)
    {
        if (template is null)
        {
            return template;
        }

        return Regex.Replace(template, _tokenRegex, tokenValue);
    }
}
Run Code Online (Sandbox Code Playgroud)

配置令牌Startup.cs(可以将其包装在扩展方法中):

services.AddControllers(options => options.Conventions.Add(
    new CustomRouteToken(
        "namespace",
        c => c.ControllerType.Namespace?.Split('.').Last()
    ));
Run Code Online (Sandbox Code Playgroud)

之后自定义令牌可用于路由:

[ApiController]
[Route("api/[namespace]")]
public class Create : ControllerBase {}
Run Code Online (Sandbox Code Playgroud)
[ApiController]
public class Get : ControllerBase 
{
    [HttpGet("api/[namespace]/{id}", Name = "[namespace]_[controller]")]
    public ActionResult Handle(int id) {}
}
Run Code Online (Sandbox Code Playgroud)