如何在 Asp.Net Core 中限制 controller action 和 area 中的参数类型

How to restrict parameter type in controller action and area in Asp.Net Core

我有 Asp.Net Core MVC 网络应用程序,其中有一个名为 AdminUI 的区域。

我的控制器是这样的:

[Area("AdminUI")]
public class ConfigurationController : BaseController
{
    public async Task<IActionResult> Client(int? id)
    {
        if (id == default)
        {
            var clientDto = _clientService.BuildClientViewModel();
            return View(clientDto);
        }

        var client = await _clientService.GetClientAsync(id.Value);
        client = _clientService.BuildClientViewModel(client);

        return View(client);
    }
}

而且我还有以下方法来添加功能以使用特定名称为整个区域添加前缀:

public static IEndpointConventionBuilder MapIdentityServer4AdminUI(
    this IEndpointRouteBuilder endpoint, string patternPrefix = "/")
{
    return endpoint.MapAreaControllerRoute("AdminUI", "AdminUI", 
        patternPrefix + "{controller=Home}/{action=Index}/{id?}");
}

如果我使用这个:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    ILoggerFactory loggerFactory)
{
    app.UseRouting();
    
    app.UseEndpoints(endpoint =>
    {
        endpoint.MapIdentityServer4AdminUI("/myPrefix/");
    });
}

我可以通过以下方式访问 AdminUI 区域 link:

https://localhost:43000/myPrefix/Configuration/Client
https://localhost:43000/myPrefix/Configuration/Client/2

它工作得很好,但现在我需要限制上面的URL,这样就可以只访问带有id为数字或没有id参数的客户端方法URL。不知道怎么实现,如果我想保留URL上面的前缀。

我已经尝试过这个解决方案,但它不起作用:

[Route("[area]/[controller]/[action]")]
[Route("[area]/[controller]/[action]/{id:int}")]
public async Task<IActionResult> Client(int id)
{
    ...
}

我可以只使用像数字这样的 ID 而没有 ID,但这会破坏 URL,因为它忽略了我上面的前缀。

URL 现在是:

https://localhost:43000/AdminUI/Configuration/Client

不是:

https://localhost:43000/myPrefix/Configuration/Client

如何实现以下行为?

[Route("[area]/[controller]/[action]")]
[Route("[area]/[controller]/[action]/{id:int}")]
public async Task<IActionResult> Client(int id)
{
    ...
}

问题与上述路由属性有关。

众所周知,Actions要么是常规路由,要么是属性路由。在控制器或动作上放置一个路由使其属性路由。定义属性路由的操作无法通过常规路由到达,反之亦然。控制器上的任何路由属性都会使控制器属性中的所有操作都被路由。

所以,通过使用上面的代码,它将使用属性路由。尝试如下更改您的代码:

[Route("myPrefix/[controller]/[action]")]
[Route("myPrefix/[controller]/[action]/{id:int}")]
public async Task<IActionResult> Client(int id)
{
    ...
}

参考:Mixed routing: Attribute routing vs conventional routing.

更新

此外,您还可以更改路由模板如下:

    public static IEndpointConventionBuilder MapIdentityServer4AdminUI(this IEndpointRouteBuilder endpoint, string patternPrefix = "/")
    {
        return endpoint.MapAreaControllerRoute("AdminUI", "AdminUI",
            patternPrefix + "{controller=Home}/{action=Index}/{id:int?}");
    }

然后,在控制器中,删除 Route 属性。

[Area("AdminUI")]
public class ConfigurationController : BaseController
{
    public async Task<IActionResult> Client(int? id)
    {
        ...             
    }
}

编辑

I would like to say this one: for whole AdminUI area use

endpoint.MapAreaControllerRoute("AdminUI", "AdminUI",
      patternPrefix + "{controller=Home}/{action=Index}/{id?}") 

and for ConfigurationController and action Client use this one:

endpoint.MapAreaControllerRoute("AdminUI", "AdminUI", patternPrefix + "{controller=Home}/{action=Index}/{id:int?}"); 

any idea, how to combine these two settings?

恐怕我们做不到(添加两个路由模板:一个用于整个 AdminUI 区域,一个用于 AdminUI 区域操作方法)。因为,如果我们同时添加它们,如果 url 不匹配 action 方法的路由模板,它将使用区域路由模板。我能想到的唯一方法是为每个操作方法添加多个路由模板,而不是为整个区域添加路由模板。像这样:

endpoint.MapAreaControllerRoute("AdminUI", "AdminUI", patternPrefix + "Home/Index2/{id:int}");

[注意]以上路由仅针对Home控制器Index2动作方法。

或使用路由属性。