WebAPI 中具有默认根参数的属性路由
Attribute routing with default root parameter in WebAPI
我有一个 API,其中所有方法都需要一个固定参数 {customer} :
/cust/{customerId}/purchases
/cust/{customerId}/invoices
/cust/{customerId}/whatever*
如何映射所有控制器以默认以可重用的方式接收此参数,例如:
endpoints.MapControllerRoute(name: "Default", pattern: "/cust/{customerId:int}/{controller}*"
我在启动时使用 .net core 3.0 和新的 .useEndpoints 方法。
您可以创建 ControllerModelConvention
to custom the attribute route behavior. For more details, see official docs 的实现。
例如,假设您想将属性路由约定(如/cust/{customerId:int}/[controller]
)与全局现有属性组合,只需创建一个约定如下:
public class FixedCustomIdControllerConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
var customerRouteModel= new AttributeRouteModel(){
Template="/cust/{customerId:int}",
};
var isApiController= controller.ControllerType.CustomAttributes.Select(c => c.AttributeType)
.Any(a => a == typeof(ApiControllerAttribute));
foreach (var selector in controller.Selectors)
{
if(!isApiController)
{
var oldAttributeRouteModel=selector.AttributeRouteModel;
var newAttributeRouteModel= oldAttributeRouteModel;
if(oldAttributeRouteModel != null){
newAttributeRouteModel= AttributeRouteModel.CombineAttributeRouteModel(customerRouteModel, oldAttributeRouteModel);
}
selector.AttributeRouteModel=newAttributeRouteModel;
} else{
// ApiController won't honor the by-convention route
// so I just replace the template
var oldTemplate = selector.AttributeRouteModel.Template;
if(! oldTemplate.StartsWith("/") ){
selector.AttributeRouteModel.Template= customerRouteModel.Template + "/" + oldTemplate;
}
}
}
}
}
然后在Startup中注册:
services.AddControllersWithViews(opts =>{
opts.Conventions.Add(new FixedCustomIdControllerConvention());
});
演示
假设我们有一个 ValuesController
:
[Route("[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get(int customerId)
{
return Json(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post(int customerId)
{
return Json(new {customerId});
}
[HttpPost("/rst")]
public IActionResult PostRst(int customerId)
{
return Json(new {customerId});
}
}
注册以上FixedCustomIdControllerConvention
后,路由行为为:
- HTTP 请求
GET https://localhost:5001/cust/123/values
将匹配 Get(int customerId)
方法。
- HTTP 请求
POST https://localhost:5001/cust/123/values/opq
将匹配 Post(int customerId)
方法
- 因为我们有意在
/rst
中放置了一个前导斜杠,所以忽略了全局约定。结果,POST https://localhost:5001/rst
将匹配 PostRst(int customerId)
方法(customId=0)
如果您使用的是带有 [ApiController]
注释的控制器:
[ApiController]
[Route("[controller]")]
public class ApiValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("/apirst")]
public IActionResult PostRst([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
}
您可能需要使用 [FromRoute]
.
修饰路由参数
我有一个 API,其中所有方法都需要一个固定参数 {customer} :
/cust/{customerId}/purchases
/cust/{customerId}/invoices
/cust/{customerId}/whatever*
如何映射所有控制器以默认以可重用的方式接收此参数,例如:
endpoints.MapControllerRoute(name: "Default", pattern: "/cust/{customerId:int}/{controller}*"
我在启动时使用 .net core 3.0 和新的 .useEndpoints 方法。
您可以创建 ControllerModelConvention
to custom the attribute route behavior. For more details, see official docs 的实现。
例如,假设您想将属性路由约定(如/cust/{customerId:int}/[controller]
)与全局现有属性组合,只需创建一个约定如下:
public class FixedCustomIdControllerConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
var customerRouteModel= new AttributeRouteModel(){
Template="/cust/{customerId:int}",
};
var isApiController= controller.ControllerType.CustomAttributes.Select(c => c.AttributeType)
.Any(a => a == typeof(ApiControllerAttribute));
foreach (var selector in controller.Selectors)
{
if(!isApiController)
{
var oldAttributeRouteModel=selector.AttributeRouteModel;
var newAttributeRouteModel= oldAttributeRouteModel;
if(oldAttributeRouteModel != null){
newAttributeRouteModel= AttributeRouteModel.CombineAttributeRouteModel(customerRouteModel, oldAttributeRouteModel);
}
selector.AttributeRouteModel=newAttributeRouteModel;
} else{
// ApiController won't honor the by-convention route
// so I just replace the template
var oldTemplate = selector.AttributeRouteModel.Template;
if(! oldTemplate.StartsWith("/") ){
selector.AttributeRouteModel.Template= customerRouteModel.Template + "/" + oldTemplate;
}
}
}
}
}
然后在Startup中注册:
services.AddControllersWithViews(opts =>{
opts.Conventions.Add(new FixedCustomIdControllerConvention());
});
演示
假设我们有一个 ValuesController
:
[Route("[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get(int customerId)
{
return Json(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post(int customerId)
{
return Json(new {customerId});
}
[HttpPost("/rst")]
public IActionResult PostRst(int customerId)
{
return Json(new {customerId});
}
}
注册以上FixedCustomIdControllerConvention
后,路由行为为:
- HTTP 请求
GET https://localhost:5001/cust/123/values
将匹配Get(int customerId)
方法。 - HTTP 请求
POST https://localhost:5001/cust/123/values/opq
将匹配Post(int customerId)
方法 - 因为我们有意在
/rst
中放置了一个前导斜杠,所以忽略了全局约定。结果,POST https://localhost:5001/rst
将匹配PostRst(int customerId)
方法(customId=0)
如果您使用的是带有 [ApiController]
注释的控制器:
[ApiController]
[Route("[controller]")]
public class ApiValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("opq")]
public IActionResult Post([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
[HttpPost("/apirst")]
public IActionResult PostRst([FromRoute]int customerId)
{
return new JsonResult(new {customerId});
}
}
您可能需要使用 [FromRoute]
.