如何在控制器操作上使用多态性一种方法
How to use polymorphism one method on controller actions
我尝试将 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 核心
AmbiguousMatchException: The request matched multiple endpoints
我查看了您在操作上生成的网址,它们都是 /test
,这导致 AmbiguousMatchException
因为您的参数是 GET 并且是可选的。
我认为您可以在操作上使用相同的名称,但您需要在操作上定义 不同 ROUTE attribute
(差异 URL)。
例如。您不能在控制器操作上使用具有多态性的默认路由。
[Route("Home/About")]
MVC controllers Mapping of controllers now takes place inside
UseEndpoints.
Add MapControllers if the app uses attribute routing.
感谢 daremachine 的回答,我得以找到关于 Google
的信息
ASP.NET 核心的第一步,我们需要 class 继承 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
明智的解决方案是只使用一种采用三个参数的方法。
但是,明智的解决方案并不能提供最有趣的 Whosebug 答案,因此这里介绍了如何使用两个自定义属性执行此操作,一个声明所需的参数,另一个声明排除哪些参数:
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
属性的维护负担。
对于 asp net core 2。如果您尝试实现与网络 api 控制器相同的逻辑,则使用 Microsoft.AspNetCore.Mvc.WebApiCompatShim。此 nuget 包在 ASP.NET Core MVC 中提供与 ASP.NET Web API 2 的兼容性,以简化现有 Web API 实现的迁移。请检查此 。从 ASP.NET Core 3.0 开始,Microsoft.AspNetCore.Mvc.WebApiCompatShim 包不再可用。
我尝试将 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 核心
AmbiguousMatchException: The request matched multiple endpoints
我查看了您在操作上生成的网址,它们都是 /test
,这导致 AmbiguousMatchException
因为您的参数是 GET 并且是可选的。
我认为您可以在操作上使用相同的名称,但您需要在操作上定义 不同 ROUTE attribute
(差异 URL)。
例如。您不能在控制器操作上使用具有多态性的默认路由。
[Route("Home/About")]
MVC controllers Mapping of controllers now takes place inside UseEndpoints.
Add MapControllers if the app uses attribute routing.
感谢 daremachine 的回答,我得以找到关于 Google
的信息ASP.NET 核心的第一步,我们需要 class 继承 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
明智的解决方案是只使用一种采用三个参数的方法。
但是,明智的解决方案并不能提供最有趣的 Whosebug 答案,因此这里介绍了如何使用两个自定义属性执行此操作,一个声明所需的参数,另一个声明排除哪些参数:
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
属性的维护负担。
对于 asp net core 2。如果您尝试实现与网络 api 控制器相同的逻辑,则使用 Microsoft.AspNetCore.Mvc.WebApiCompatShim。此 nuget 包在 ASP.NET Core MVC 中提供与 ASP.NET Web API 2 的兼容性,以简化现有 Web API 实现的迁移。请检查此