ASP.NET MVC Core 中路由或查询字符串的参数绑定
Parameter binding to either route or querystring in ASP.NET MVC Core
我正在将 ASP.NET MVC (.NET Framework) Web 应用程序迁移到 ASP.NET MVC Core 3.1。该应用程序是公司内部的。我们正在借此机会清理一些 API 路由,使它们更 RESTful,例如:/api/Values?id=1
→ /api/Values/1
。但是,并非所有其他应用程序都能够在该应用程序投入生产时进行适当的更改,因此我们希望能够同时支持这两种 URL 格式。这可能吗?我的路由设置如下:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(null);
endpoints.EnableDependencyInjection();
endpoints.MapODataRoute("ODataRoute", "odata", GetEdmModel());
});
我的控制器看起来像这样:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
// constructor and dependency injection omitted
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Get(int id)
{
// method logic omitted
}
}
使用上面的代码,/api/Values/1
工作正常,但是查询字符串 ?id=1
导致 404。如果我将属性更改为 [HttpGet]
那么查询字符串工作,但是 RESTful 版本没有。到目前为止,这是我尝试过的方法:
[HttpGet("{id}")]
+ [FromQuery]
– REST: 404, QS: 405(方法不允许)
[HttpGet]
+ [FromQuery] [FromRoute]
– 休息:404,QS:200
仅 [HttpGet("{id?}")]
– 休息:200,QS:404
这可以吗?谢谢。
为了达到要求,您可以尝试定义多条到达同一动作的路由,如下所示。
[HttpGet]
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Get(int id)
{
if (Request.Query.TryGetValue("id",out StringValues qs_id))
{
int.TryParse(qs_id.FirstOrDefault(), out id);
}
//...
// method logic omitted
//for testing purpose
return Ok($"id is {id}");
}
测试结果
更新:
如果可能,您也可以尝试实现并使用URL重写规则来实现。
<rule name="id qs rule">
<match url="api/values" />
<conditions>
<add input="{PATH_INFO}" pattern="api/values$" />
<add input="{QUERY_STRING}" pattern="id=([0-9]+)" />
</conditions>
<action type="Rewrite" url="api/values/{C:1}/" appendQueryString="false" />
</rule>
测试结果
我正在将 ASP.NET MVC (.NET Framework) Web 应用程序迁移到 ASP.NET MVC Core 3.1。该应用程序是公司内部的。我们正在借此机会清理一些 API 路由,使它们更 RESTful,例如:/api/Values?id=1
→ /api/Values/1
。但是,并非所有其他应用程序都能够在该应用程序投入生产时进行适当的更改,因此我们希望能够同时支持这两种 URL 格式。这可能吗?我的路由设置如下:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(null);
endpoints.EnableDependencyInjection();
endpoints.MapODataRoute("ODataRoute", "odata", GetEdmModel());
});
我的控制器看起来像这样:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
// constructor and dependency injection omitted
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Get(int id)
{
// method logic omitted
}
}
使用上面的代码,/api/Values/1
工作正常,但是查询字符串 ?id=1
导致 404。如果我将属性更改为 [HttpGet]
那么查询字符串工作,但是 RESTful 版本没有。到目前为止,这是我尝试过的方法:
[HttpGet("{id}")]
+[FromQuery]
– REST: 404, QS: 405(方法不允许)[HttpGet]
+[FromQuery] [FromRoute]
– 休息:404,QS:200
仅 [HttpGet("{id?}")]
– 休息:200,QS:404
这可以吗?谢谢。
为了达到要求,您可以尝试定义多条到达同一动作的路由,如下所示。
[HttpGet]
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Get(int id)
{
if (Request.Query.TryGetValue("id",out StringValues qs_id))
{
int.TryParse(qs_id.FirstOrDefault(), out id);
}
//...
// method logic omitted
//for testing purpose
return Ok($"id is {id}");
}
测试结果
更新:
如果可能,您也可以尝试实现并使用URL重写规则来实现。
<rule name="id qs rule">
<match url="api/values" />
<conditions>
<add input="{PATH_INFO}" pattern="api/values$" />
<add input="{QUERY_STRING}" pattern="id=([0-9]+)" />
</conditions>
<action type="Rewrite" url="api/values/{C:1}/" appendQueryString="false" />
</rule>
测试结果