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

Posted

技术标签:

【中文标题】ASP.NET Core API:添加自定义路由令牌解析器【英文标题】:ASP.NET Core API: Add custom route token resolver 【发布时间】:2021-10-24 22:36:41 【问题描述】:

我在我的项目中使用https://github.com/ardalis/ApiEndpoints(每个控制器一个操作),我遇到了[Route("[controller]")] 并不适合我的问题,因为控制器看起来像这样:

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

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

我目前的解决方案

硬编码路线 创建包含自定义标记的路由的自定义属性,以及解析自定义标记并生成Route 属性的源生成器。

【问题讨论】:

【参考方案1】:

非常感谢@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);
    

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

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

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

[ApiController]
[Route("api/[namespace]")]
public class Create : ControllerBase 
[ApiController]
public class Get : ControllerBase 

    [HttpGet("api/[namespace]/id", Name = "[namespace]_[controller]")]
    public ActionResult Handle(int id) 

【讨论】:

【参考方案2】:

您可以通过实现IApplicationModelConvention 来实现这一点。更多信息在这里:Custom routing convention

public class NamespaceRoutingConvention : IApplicationModelConvention

    public void Apply(ApplicationModel application)
    
        foreach (var controller in application.Controllers)
        
            controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
            
                Template = controller.ControllerType.Namespace.Replace('.', '/') + "/[controller]"
            ;
            
        
    

然后在startup.cs中添加

public void ConfigureServices(IServiceCollection services)

    services.AddMvc(options =>
    
        options.Conventions.Add(new NamespaceRoutingConvention());
    );

【讨论】:

【参考方案3】:

区域正是您所需要的。如 .net 核心文档中所述:

区域是用于组织相关功能的 ASP.NET 功能 作为一个单独的组:

路由命名空间。 视图和 Razor 页面的文件夹结构。

要使用区域路线,您只需将它们添加到启动中。

像这样:

app.UseEndpoints(endpoints =>

    endpoints.MapControllerRoute(
        name: "MyArea",
        pattern: "area:exists/controller=Home/action=Index/id?");

    endpoints.MapControllerRoute(
        name: "default",
        pattern: "controller=Home/action=Index/id?");
);

之后,路线将遵循您在 Areas 文件夹中的文件夹结构。 如需更多信息,请访问Areas in ASP.NET Core 文档。

【讨论】:

您能否阐明为 Api 控制器启用区域路由所需的步骤? ASP.NET Core 要求 Api 控制器具有 [Route] 属性,而 [Route("[area]")] 不起作用,因为它无法识别 area 令牌(即使在像您显示的那样映射路由之后)。 @МаксимКошевой 主要步骤是从您的项目中删除 APIEndPoints。在此之后一切都会正常工作 我的项目是一个 API,因此从控制器中删除 [ApiController] 标记只是为了更容易路由不是一种选择。 ASP.NET 需要 [Route] 是有原因的,我同意。我需要的是一种扩展此属性以解析自定义令牌的方法,而不是摆脱它。

以上是关于ASP.NET Core API:添加自定义路由令牌解析器的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口

ASP.NET Web Api 路由自定义

ASP.NET Core Web API

如何通过 ASP.NET Core 中的 api 控制器更新 IdentityUser 的自定义属性?

如何自定义 ASP.Net Core 模型绑定错误?

向 ASP.NET Core 的 JWT 令牌添加自定义验证?