Как использовать метод полиморфизма One для действий контроллера

Я попытался преобразовать 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: запрос соответствует нескольким конечным точкам

4 ответа

Решение

Разумное решение - иметь только один метод, который принимает три параметра.

Но разумные решения не позволяют получить наиболее интересные ответы на вопросы stackru, поэтому вот как вы можете сделать это с помощью двух настраиваемых атрибутов, в одном из которых указываются требуемые параметры, а в другом - какие параметры исключены:

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 атрибут на других методах.

Я смотрю ваши сгенерированные URL-адреса на действия, и они оба /test что вызывает AmbiguousMatchException потому что ваши параметры GET и не являются обязательными.

Я думаю, у вас могут быть одинаковые имена для действий, но вам нужно определить разные ROUTE attribute(различаются URL-адреса) по действиям.

Например. вы не можете использовать маршрут по умолчанию с полиморфизмом для действий контроллера.

[Route("Home/About")]

Контроллеры MVC Отображение контроллеров теперь происходит внутри UseEndpoints.

Добавьте MapControllers, если приложение использует маршрутизацию атрибутов.

Источник

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

Благодаря daremachine с его ответом я смог найти информацию в 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 в мой вопрос

Для asp net core 2. Если вы попытаетесь реализовать ту же логику, что и в контроллерах веб-API, используйте Microsoft.AspNetCore.Mvc.WebApiCompatShim. Этот пакет nuget обеспечивает совместимость в ASP.NET Core MVC с ASP.NET Web API 2, чтобы упростить миграцию существующих реализаций веб-API. Пожалуйста, проверьте этот ответ . Начиная с ASP.NET Core 3.0 пакет Microsoft.AspNetCore.Mvc.WebApiCompatShim больше не доступен.

Другие вопросы по тегам