WebAPI中具有默认根参数的属性路由

时间:2019-12-23 19:17:45

标签: rest asp.net-core asp.net-mvc-routing asp.net-core-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方法。

1 个答案:

答案 0 :(得分:1)

您可以创建ControllerModelConvention的实现以自定义属性路由行为。有关更多详细信息,请参见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;
                }
            }
        }
    }
}

然后在启动中注册它:

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后,路由行为为:

  1. HTTP请求GET https://localhost:5001/cust/123/values将与Get(int customerId)方法匹配。
  2. HTTP请求POST https://localhost:5001/cust/123/values/opq将与Post(int customerId)方法匹配
  3. 因为我们有意在/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]装饰路由中的参数。