ASP.NET CORE - 使用 [ApiController] 动态添加属性路由到控制器

问题描述 投票:0回答:1

我希望控制器具有由自定义代码生成的默认路由 URL 和 HTTP 方法,并且不需要任何

HttpPost|Get|etc
属性(路由操作将默认为
Post
)。我可以使用
Microsoft.AspNetCore.Mvc.ApplicationModels.IActionModelConvention
来做到这一点。

但是我无法在控制器上使用

[ApiController]
,因为没有显式属性路由 - 我收到错误:

Controller Action>没有属性route。用 ApiControllerAttribute 注释的控制器上的操作方法必须是属性路由

有没有办法动态添加默认路由和http方法到控制器操作并使用

[ApiController]
属性?

相关问题

ASP.NET Core API 中的传统路由 - 这很接近 - 但我想拥有

[ApiController]
的所有优点/默认值 - 即不必为参数设置
[FromBody]

核心要求

我想创建一个 OpenAPI 后端并用于前端的 JS SPA 客户端代码生成。由于 API 仅由 SPA 使用,并且客户端 API 将自动生成代码 - 我并不真正关心硬编码路由,甚至不关心使用“正确”的 HTTP 方法(一切都可能只是 POST)。所以我想摆脱控制器中的所有样板。

当前的想法/想法

在执行

I*Convention
验证之前,不会应用
Microsoft.AspNetCore.Mvc.ApplicationModels.ApiBehaviorApplicationModelProvider.EnsureActionIsAttributeRouted
。因此,即使我在约定中应用了
AttributeRouteModel
,它也不包含在验证中:

actionSelector.AttributeRouteModel = new AttributeRouteModel
                {
                    Template = $"foo/bar/{Guid.NewGuid():N}",
                };

我的下一个想法是实现某种

Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelProvider
(这就是
[ApiController]
似乎是如何验证的 - 请参阅:
Microsoft.AspNetCore.Mvc.ApplicationModels.ApiBehaviorApplicationModelProvider
) - 并尝试确保它在
ApiBehaviorApplicationModelProvider
之前注册并执行?

如果这不起作用,那么似乎可以选择更深入地研究诸如

Microsoft.AspNetCore.Mvc.Abstractions.IActionDescriptorProvider
之类的东西 - 但这看起来非常可怕。

交叉发布:https://github.com/dotnet/aspnetcore/discussions/52526

asp.net-core asp.net-core-webapi asp.net-mvc-routing
1个回答
0
投票

我最终使用了

IApplicationModelProvider
策略。我只需确保将
Order
属性设置为在
ApiBehaviorApplicationModelProvider
之前运行 - 所以我最终得到:

/// <summary>
/// [ApiController] attribute model provider uses <c>-1000 + 100</c> -
/// this needs to be applied first so that we get attribute routing.
/// <see cref="AuthorizationApplicationModelProvider"/> uses +10 - so we're trying
/// to split the difference here.
/// </summary>
public int Order => -1000 + 50;

而且,如果有人感兴趣,以下是我如何将路由和 HttpMethods 添加到操作中(它似乎有效......):

public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
    var actionSelectors = context.Result.Controllers
        .SelectMany(
            c => c.Actions.SelectMany(
                a => a.Selectors.Select(
                    selector => (
                        controller: c,
                        action: a, selector))));

    foreach (var actionSelector in actionSelectors)
    {
        var selectorModel = actionSelector.selector;
        var actionDebugName = $"{actionSelector.controller.ControllerName}:{actionSelector.action.ActionName}";

        if (selectorModel.AttributeRouteModel == null)
        {
            var endpointUrl = // ... magic here to create the URL

            _logger.LogDebug(
                "Endpoint has no attribute route - adding default ({Action}): {Url}",
                actionDebugName,
                endpointUrl);

            selectorModel.AttributeRouteModel = new AttributeRouteModel
            {
                Template = endpointUrl,
            };
        }
        else
        {
            _logger.LogInformation(
                "{Endpoint}: Endpoint has existing attribute route - will not add a default",
                actionDebugName);
        }

        /*
         * Add a default HTTP Method to the action (POST) if not specified
         */
        var httpMethodConstraint =
            (HttpMethodActionConstraint?)selectorModel.ActionConstraints.FirstOrDefault(
                c => c is HttpMethodActionConstraint);

        /*
         * Debug out the HTTP Methods
         */
        var httpMethods = httpMethodConstraint == null
            ? "NONE"
            : string.Join(",", httpMethodConstraint.HttpMethods);

        _logger.LogDebug("Existing HTTP Methods: {HttpMethods}", httpMethods);
        
        if (httpMethodConstraint == null)
        {
            _logger.LogDebug("{Endpoint}: Adding default http method", actionDebugName);
            selectorModel.ActionConstraints.Add(
                new HttpMethodActionConstraint(new[] { DefaultHttpMethod }));
        }
        else
        {
            _logger.LogInformation("{Endpoint}: already has http action", actionDebugName);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.