ASP 核心 3.1 路由
ASP Core 3.1 Routing
我在一个控制器中有 2 个 HttpGet
端点。为了使路由不同,我向其中一个添加了参数。看下一段代码
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("{id:decimal}")]
public async Task<IActionResult> Get(decimal id)
{
var user = await User.GetAsync(id);
return Ok(user);
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var users = await User.GetAllAsync();
return Ok(users);
}
}
问题是无法访问第一个端点。即使我在查询 http://localhost:80/api/user?id=1
中有 id
参数,我也达到了第二个端点。
期望的行为是
- 请求
http://localhost:80/api/user?id=1
-> 第一个端点
- 请求
http://localhost:80/api/user
-> 第二个端点
这一定是一些愚蠢简单的事情,因为我确定我以前也是这样做的,但现在我卡住了
当你使用
[HttpGet("{id:decimal}")]
这意味着你的Url就像
http://localhost:80/api/user/{id}
或
http://localhost:80/api/user/1
Desired behavior is
1.Request http://localhost:80/api/user?id=1
-> first endpoint
2.Request http://localhost:80/api/user
-> second endpoint
如果您想匹配请求并将它们映射到基于查询字符串的预期操作,您可以尝试实现自定义 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);
}
}
}
应用于操作
[HttpGet]
[QueryStringConstraintAttribute("id",true)]
[QueryStringConstraintAttribute("", false)]
public async Task<IActionResult> Get([FromQuery]decimal id)
{
//var user = await User.GetAsync(id);
//return Ok(user);
//for test purpose
return Ok("ActionWithQueryString");
}
[HttpGet]
[QueryStringConstraintAttribute("id", false)]
[QueryStringConstraintAttribute("", true)]
public async Task<IActionResult> GetAll()
{
//var users = await User.GetAllAsync();
//return Ok(users);
//for test purpose
return Ok("ActionWithOutQueryString");
}
测试结果
我在一个控制器中有 2 个 HttpGet
端点。为了使路由不同,我向其中一个添加了参数。看下一段代码
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("{id:decimal}")]
public async Task<IActionResult> Get(decimal id)
{
var user = await User.GetAsync(id);
return Ok(user);
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var users = await User.GetAllAsync();
return Ok(users);
}
}
问题是无法访问第一个端点。即使我在查询 http://localhost:80/api/user?id=1
中有 id
参数,我也达到了第二个端点。
期望的行为是
- 请求
http://localhost:80/api/user?id=1
-> 第一个端点 - 请求
http://localhost:80/api/user
-> 第二个端点
这一定是一些愚蠢简单的事情,因为我确定我以前也是这样做的,但现在我卡住了
当你使用
[HttpGet("{id:decimal}")]
这意味着你的Url就像
http://localhost:80/api/user/{id}
或
http://localhost:80/api/user/1
Desired behavior is
1.Request
http://localhost:80/api/user?id=1
-> first endpoint
2.Request
http://localhost:80/api/user
-> second endpoint
如果您想匹配请求并将它们映射到基于查询字符串的预期操作,您可以尝试实现自定义 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);
}
}
}
应用于操作
[HttpGet]
[QueryStringConstraintAttribute("id",true)]
[QueryStringConstraintAttribute("", false)]
public async Task<IActionResult> Get([FromQuery]decimal id)
{
//var user = await User.GetAsync(id);
//return Ok(user);
//for test purpose
return Ok("ActionWithQueryString");
}
[HttpGet]
[QueryStringConstraintAttribute("id", false)]
[QueryStringConstraintAttribute("", true)]
public async Task<IActionResult> GetAll()
{
//var users = await User.GetAllAsync();
//return Ok(users);
//for test purpose
return Ok("ActionWithOutQueryString");
}
测试结果