【问题标题】:How to use polymorphism one method on controller actions如何在控制器动作上使用多态性一种方法
【发布时间】:2020-03-03 12:09:48
【问题描述】:

我尝试将 ASP.NET WEB API 转换为 ASP.NET CORE WEB API 并出现错误

我在 ASP.NET WebAPI 中的代码

public class TestController : ApiController
{
    // GET /test
    public object Get()
    {
        return "get";
    }

    // GET /test?id={id}
    public object Get(string id)
    {
        return id;
    }

    // GET /test?id={id}&anyParam={anyParam}
    public object Get(string id, string anyParam)
    {
        return id + anyParam;
    }
}

config.Routes.MapHttpRoute("Controller", "{controller}");

尝试将其转换为 ASP.NET Core 2.1 / 3.0

[ApiController]
[Route("{controller}")]
public class TestController : ControllerBase
{
    // GET /test
    public object Get()
    {
        return "get";
    }

    // GET /test?id={id}
    public object Get(string id)
    {
        return id;
    }

    // GET /test?id={id}&anyParam={anyParam}
    public object Get(string id, string anyParam)
    {
        return id + anyParam;
    }
}

services.AddControllers(); 
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });

我在 ASP.NET Core 中有

AmbiguousMatchException:请求匹配多个端点

【问题讨论】:

  • 看看这个endpoints.MapControllerRoute in docs.microsoft.com/cs-cz/aspnet/core/migration/…
  • @daremachine 尝试与endpoints.MapControllerRoute("Controller", "{controller}"); 相同的错误AmbiguousMatchException:请求匹配多个端点
  • 我希望他们在 Web API 中抛出异常,因为在 Swagger 定义中具有相同名称但不同参数的多个端点是不合法的。解决此问题的最佳方法是为每个操作指定不同的名称或将每个操作移至新控制器。
  • 不应该是[controller]而不是{controller}吗?只是我的快速印象。
  • @Kit 我发现没关系

标签: asp.net-core asp.net-core-webapi asp.net-core-2.1 asp.net-web-api-routing


【解决方案1】:

明智的解决方案是只使用一种采用三个参数的方法。

但是,明智的解决方案并不能提供最有趣的 stackoverflow 答案,因此您可以使用两个自定义属性来做到这一点,一个说明需要的参数,另一个说明排除哪些参数:

public class RequireRequestParameterAttribute : ActionMethodSelectorAttribute
{
    private readonly string[] _requiredNames;
    public RequireRequestParameterAttribute(params string[] names)
    {
        this._requiredNames = names;
    }
    public override bool IsValidForRequest(
        RouteContext routeContext,
        ActionDescriptor action
    ) =>
        this._requiredNames
            .All(
                routeContext
                   .HttpContext
                   .Request
                   .Query
                   .ContainsKey
            );
}

public class DisallowRequestParameterAttribute : ActionMethodSelectorAttribute
{
    private readonly string[] _forbiddenNames;
    public DisallowRequestParameterAttribute(params string[] names)
    {
        this._forbiddenNames = names;
    }
    public override bool IsValidForRequest(
        RouteContext routeContext,
        ActionDescriptor action
    ) =>
        !(this._forbiddenNames
             .Any(
                 routeContext
                     .HttpContext
                     .Request
                     .Query
                     .ContainsKey
              )
        );
}

现在您可以按如下方式应用属性:

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    // GET test
    public object Get()
    {
        return "Get";
    }


    // GET test?id={id}
    [RequireRequestParameter("id")]
    [DisallowRequestParameter("anyParam")]
    public object Get(string id)
    {
        return id;
    }

    // GET test?id={id}&anyParam={anyParam}
    [RequireRequestParameter("id", "anyParam")]
    public object Get(string id, string anyParam)
    {
        return $"{id}: {anyParam}";
    }
}

这意味着,如果您使用第三个参数添加另一个方法,您将承担在其他方法上添加或修改DisallowRequestParameter 属性的维护负担。

【讨论】:

  • 谢谢,找到了一个类似的解决方案,但你有它的工作和紧凑。我不会只在一种方法中使用 3 个参数,因为这在查询方面会很丑陋。我希望这对其他人有帮助
【解决方案2】:

我查看了您生成的有关操作的 url,它们都是 /test 导致 AmbiguousMatchException 因为您的参数是 GET 并且是可选的。

我认为您可以在操作上使用相同的名称,但您需要在操作上定义 不同 ROUTE attribute(差异网址)。

例如。您不能在控制器操作上使用具有多态性的默认路由。

[Route("Home/About")]

MVC 控制器控制器的映射现在发生在内部 使用端点。

如果应用使用属性路由,则添加 MapControllers。

Source

https://docs.microsoft.com/cs-cz/aspnet/core/mvc/controllers/routing?view=aspnetcore-3.0#attribute-routing

【讨论】:

  • 谢谢,等别人的回答,突然我的问题有了使用多态的解决方案
  • 好的,但是您的解决方案是具有唯一的操作名称或设置不同的路线甚至路线属性。您需要区分具有相同动作名称的动作端点。
【解决方案3】:

感谢dalemachine 的回答,我能够在Google 上找到信息

在 ASP.NET Core 中,我们需要继承 ActionMethodSelectorAttribute 的类

public class RequireRequestValueAttribute : ActionMethodSelectorAttribute
{
    public RequireRequestValueAttribute(string name, string value = null)
    {
        Name = name;
        Value = value;
    }

    public string Name { get; }
    public string Value { get; }

    public StringComparison ComparisonType { get; } = StringComparison.OrdinalIgnoreCase;

    private bool ValueIsValid(object value)
    {
        return ValueIsValid(value?.ToString());
    }
    private bool ValueIsValid(string value)
    {
        if (Value == null)
        {
            return true;
        }

        return string.Equals(value, Value, ComparisonType);
    }

    public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
    {
        var value = default(object);

        if (routeContext.RouteData.Values.TryGetValue(Name, out value) && ValueIsValid(value))
            return true;

        if (routeContext.RouteData.DataTokens.TryGetValue(Name, out value) && ValueIsValid(value))
            return true;

        if (routeContext.HttpContext.Request.Query.ContainsKey(Name))
        {
            var values = routeContext.HttpContext.Request.Query[Name];
            if (values.Count <= 0)
            {
                if (ValueIsValid(null))
                    return true;
            }
            else if (values.Any(v => ValueIsValid(v)))
                return true;
        }

        return false;
    }
}

然后我们可以添加到问题方法[RequireRequestValue("")],控制器会是这样的

[ApiController]
[Route("{controller}")]
public class TestController : ControllerBase
{
    // GET /test
    public object Get()
    {
        return "get";
    }

    // GET /test?id={id}
    [RequireRequestValue("id")]
    public object Get(string id)
    {
        return id;
    }
}

但它不能多态两个相似的字段,在我的问题中输入 id

【讨论】:

    【解决方案4】:

    对于 asp net core 2。如果您尝试实现与 Web api 控制器中相同的逻辑,请使用 Microsoft.AspNetCore.Mvc.WebApiCompatShim。此 nuget 包提供 ASP.NET Core MVC 与 ASP.NET Web API 2 的兼容性,以简化现有 Web API 实现的迁移。请检查此answer。从 ASP.NET Core 3.0 开始,Microsoft.AspNetCore.Mvc.WebApiCompatShim 包不再可用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      • 2019-07-15
      • 2013-06-17
      • 1970-01-01
      • 2018-01-29
      • 1970-01-01
      相关资源
      最近更新 更多