![](/img/trans.png)
[英]How to handle multiple endpoints in ASP.Net Core 3 Web API properly
[英]Handle multiple endpoints in .NET Core 3.1 Web API by Query Params
我正在将控制器从 .NET 框架迁移到 .NET 核心,并且我希望与以前版本的 API 调用兼容。 我在处理来自查询参数的多个路由时遇到问题。
我的示例 controller:
[Route("/api/[controller]")]
[Route("/api/[controller]/[action]")]
public class StaticFileController : ControllerBase
{
[HttpGet("{name}")]
public HttpResponseMessage GetByName(string name)
{
}
[HttpGet]
public IActionResult Get()
{
}
}
调用api/StaticFile?name=someFunnyName
将引导我执行Get()
操作,而不是预期的GetByName(string name)
。
我想要达到的目标:
api/StaticFile
-> 转到Get()
操作api/StaticFile?name=someFunnyName
-> 转到GetByName()
操作我来自Startup.cs
的app.UseEndpoints()
只有以下几行:
endpoints.MapControllers();
endpoints.MapDefaultControllerRoute();
如果我在任何地方使用[HttpGet]
并添加([FromQuery] string name)
它会让我AmbiguousMatchException: The request matched multiple endpoints
感谢您花时间帮助我(也许还有其他人)
HttpGet
的参数设置路由,而不是查询字符串参数名称。
您应该为操作参数添加FromQuery
属性并使用不带"{name}"
的HttpGet
:
[HttpGet]
public HttpResponseMessage GetByName([FromQuery] string name)
{
// ...
}
您还可以为查询参数设置不同的名称:
[HttpGet]
public HttpResponseMessage GetByName([FromQuery(Name = "your_query_parameter_name")] string name)
{
// ...
}
但是现在你有两个动作匹配相同的路由,所以你会得到异常。 仅基于查询字符串部分(路径相同)执行不同逻辑的唯一方法是检查操作中的查询字符串:
[HttpGet]
public IActionResult Get([FromQuery] string name)
{
if (name == null)
{
// execute code when there is not name in query string
}
else
{
// execute code when name is in query string
}
}
因此,您只有一个操作可以使用相同的路线处理这两种情况。
我想要达到的目标:
- 调用 GET api/StaticFile -> 转到 Get() 操作
- 调用 GET api/StaticFile?name=someFunnyName -> 转到 GetByName() 操作
要实现上述基于查询字符串将请求与预期操作匹配的要求,您可以尝试实现自定义ActionMethodSelectorAttribute并将其应用于您的操作,如下所示。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class QueryStringConstraintAttribute : ActionMethodSelectorAttribute
{
public string QueryStingName { get; set; }
public bool CanPass { get; set; }
public QueryStringConstraintAttribute(string qname, bool canpass)
{
QueryStingName = qname;
CanPass = canpass;
}
public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
{
StringValues value;
routeContext.HttpContext.Request.Query.TryGetValue(QueryStingName, out value);
if (QueryStingName == "" && CanPass)
{
return true;
}
else
{
if (CanPass)
{
return !StringValues.IsNullOrEmpty(value);
}
return StringValues.IsNullOrEmpty(value);
}
}
}
应用于操作
[Route("api/[controller]")]
[ApiController]
public class StaticFileController : ControllerBase
{
[HttpGet]
[QueryStringConstraint("name", true)]
[QueryStringConstraint("", false)]
public IActionResult GetByName(string name)
{
return Ok("From `GetByName` Action");
}
[HttpGet]
[QueryStringConstraint("name", false)]
[QueryStringConstraint("", true)]
public IActionResult Get()
{
return Ok("From `Get` Action");
}
}
测试结果
我的解决方案来自https://www.strathweb.com/2016/09/required-query-string-parameters-in-asp-net-core-mvc/
public class RequiredFromQueryAttribute : FromQueryAttribute, IParameterModelConvention
{
public void Apply(ParameterModel parameter)
{
if (parameter.Action.Selectors != null && parameter.Action.Selectors.Any())
{
parameter.Action.Selectors.Last().ActionConstraints.Add(new RequiredFromQueryActionConstraint(parameter.BindingInfo?.BinderModelName ?? parameter.ParameterName));
}
}
}
public class RequiredFromQueryActionConstraint : IActionConstraint
{
private readonly string _parameter;
public RequiredFromQueryActionConstraint(string parameter)
{
_parameter = parameter;
}
public int Order => 999;
public bool Accept(ActionConstraintContext context)
{
if (!context.RouteContext.HttpContext.Request.Query.ContainsKey(_parameter))
{
return false;
}
return true;
}
}
例如,如果在StaticFileController
中使用[RequiredFromQuery]
我们可以调用/api/StaticFile?name=withoutAction
和/api/StaticFile/GetByName?name=wAction
但不能调用/api/StaticFile/someFunnyName
(?name= and /)
解决方法是创建单独的 controller 操作来处理此类请求
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.