Как использовать метод полиморфизма 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 больше не доступен.