ASP.NET Core Web API:通过查询参数进行路由

6

我来自Java/Spring的背景,现在尝试将一些知识转移到ASP.NET Core 6。

在Spring中,可以在RestController上根据查询参数的存在路由请求。

因此,具有uri:/students?firstName=KevinHttpRequest可以与具有uri:/studentsHttpRequest路由到不同的控制器方法。

在ASP.NET Core 6中,经过一些示例和阅读Web API文档后,我无法确定是否可能实现相当的功能。

以下是我想要实现的内容,在使用两种方法和路由配置时,能否根据查询参数确定要调用哪个控制器方法?

 [ApiController]
 [Route("Students")]
 public class StudentHomeProfileController : ControllerBase
 {
    [HttpGet] //Route here when no parameters provided
    public async Task<ActionResult<IEnumerable<Student>>> GetStudentAsync()
    {
        /* Code omitted */
    }

    [HttpGet] //Route here when firstName query param provided
    public async Task<ActionResult<IEnumerable<Student>>> SearchStudentAsync([FromQuery] string firstName)
    {
        /* Code omitted */
    }
 }

你可以像这样做:[HttpGet("SearchStudentAsync")],它将路由到students/searchstudentasync。个人而言,我曾经搜索过一种在Http方法属性中不使用路由参数的方法,但是我从未找到过。 - AchoVasilev
感谢@AchoVasilev。我想我可以调整路由,但是我尽可能地想保持RESTFUL。我有一个原型,将[HttpGet("search")]添加到第二个方法中。我相信我需要使用这种方法来处理每个搜索查询参数的存在或缺失。 - Kevin Bowersox
试着看看这个 -> https://stackoverflow.com/Questions/9499794/single-controller-with-multiple-get-methods-in-asp-net-web-api。也许会有所帮助。 - AchoVasilev
为什么不尝试子控制器路由方法?子控制器允许您创建像order\1\orderdetail\2这样的路由。不要使用查询参数进行路由,使用控制器和子控制器。基于参数传递的动态路由将在UI中造成混乱。 - Golden Lion
@GoldenLion 我不是在尝试通过ID来定位特定的学生。我是在尝试搜索具有特定属性值的学生。例如,所有名字为Kevin的学生或者在某个年级范围内的学生。 - Kevin Bowersox
为什么不使用带有FromBody参数的Post路由,而不使用查询字符串? - Golden Lion
4个回答

14

虽然ASP.NET Core没有默认提供按查询参数过滤的功能,但你可以很容易地自行提供此功能。

在可扩展性方面,ASP.NET有一些超能力之一是IActionConstraint,它

支持条件逻辑以确定是否应选择关联操作以用于给定请求。(来源)

创建一个用于按查询参数过滤的注释非常简单,只需:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class QueryParameterConstraintAttribute : Attribute, IActionConstraint
{
    private readonly string _parameterName;

    public QueryParameterConstraintAttribute(string parameterName)
    {
        this._parameterName = parameterName;
    }

    public bool Accept(ActionConstraintContext context)
    {
        return context.RouteContext.HttpContext.Request.Query.Keys.Contains(this._parameterName);
    }

    public int Order { get; }
}

现在唯一剩下的就是在你的控制器方法上加上这个限制条件

[HttpGet] //Route here when firstName query param provided
[QueryParameterConstraint("firstName")]
public async Task<ActionResult<IEnumerable<Student>>> SearchStudentAsync([FromQuery] string firstName)
{
    /* Code omitted */
}

在快速测试中,我能够确认它似乎按预期工作,即使您为不同的查询参数添加多个这些属性(如果所有条件匹配,则调用路由)。

(请注意,这是使用.NET Core 2.1进行测试的。无论如何,与.NET 6几乎相同)


2
我知道已经有一个被接受的答案了,但是我还是想提供我的解决方案,以防其他人需要。 - Paul Kertscher
您还可以添加 IActionModelConvention,它将执行类似于 action.Selectors[0].ActionConstraints.Add(new QueryParameterConstraintAttribute(action.Parameters[0].Name)) 的操作,以消除每个操作上的属性。 - OwnageIsMagic

5

我认为您在寻找类似于这样的内容,需要在 "HttpGet" 属性中指定参数。

https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/routing?view=aspnetcore-6.0#attribute-routing-with-http-verb-attributes

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

1
所以这将转换为基于路由的参数。我希望坚持使用查询参数,因为最多会有3个。 - Kevin Bowersox
1
你的意思是这样吗? [HttpGet("{parameter1}") [HttpGet("{parameter2}") [HttpGet("{parameter3}") - rufiooo

3

我修改了正确的链接。虽然在你的问题中没有看到“.net core”,但我的答案仍然适用于任何特定的框架。 - Ran Turner
谢谢,我同意。我正在尝试将一个从Spring到ASP.NET的概念进行映射,但似乎不可能。 - Kevin Bowersox
祝你好运,我的朋友 @KevinBowersox。 - Ran Turner

0
ASP.Net Framework Web API 2 能够根据查询参数选择一个动作。请参阅action selection algorithm 3.b
要仅通过查询参数选择一个动作,需要一个空字符串的路由属性 [Route("")]
愚蠢的 .Net Core

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接