[Route("api/[controller]")]
public class OrdersController<T> : Controller where T : IOrder
{
[HttpPost("{orderType}")]
public async Task<IActionResult> Create(
[FromBody] Order<T> order)
{
//....
}
}
我希望{orderType} URI段变量能够控制控制器的通用类型。我正在尝试使用自定义的
IControllerFactory
和IControllerActivator
,但是没有成功。每次我尝试发送请求时,都会收到404响应。我的自定义控制器工厂(和激活器)的代码从未执行过。显然问题在于ASP.NET Core期望有效的控制器以后缀“Controller”结尾,但我的通用控制器以(反射为基础的)后缀“Controller`1”结尾。因此,它声明的基于属性的路由被忽略了。在ASP.NET MVC中,至少在早期,默认控制器工厂DefaultControllerFactory负责发现所有可用的控制器。它测试了“Controller”后缀:
显然,在ASP.NET Core中,控制器工厂不再负责此任务。正如我之前所述,我的自定义控制器工厂对于“普通”控制器执行,但对于通用控制器从未被调用。因此,还有其他一些早期评估过程中的东西来管理控制器的发现。MVC框架提供了一个默认的控制器工厂(名为DefaultControllerFactory),它将搜索应用程序域中的所有程序集,查找所有实现IController接口且名称以“Controller”结尾的类型。
是否有人知道负责该发现的“服务”接口是什么?我不知道自定义接口或“钩子”点。
还有人知道让ASP.NET Core“转储”其发现的所有控制器名称的方法吗?编写单元测试来验证任何我期望的自定义控制器发现是否有效将非常好。
顺便说一句,如果有一个“钩子”允许发现通用控制器名称,则意味着路由替换也必须标准化:
[Route("api/[controller]")]
public class OrdersController<T> : Controller { }
无论给定什么值作为T
,[controller]名称必须保持简单的基本泛型名称。以上面的代码为例,[controller]的值将是"Orders"。它不会是"Orders`1"或"OrdersOfSomething"。
注意
这个问题也可以通过显式声明闭合泛型类型来解决,而不是在运行时生成它们:
public class VanityOrdersController : OrdersController<Vanity> { }
public class ExistingOrdersController : OrdersController<Existing> { }
以上方法可行,但它生成的URI路径我不喜欢:
~/api/VanityOrders
~/api/ExistingOrders
我实际想要的是这个:
~/api/Orders/Vanity
~/api/Orders/Existing
另一个调整可以让我得到我所寻找的URI:
[Route("api/Orders/Vanity", Name ="VanityLink")]
public class VanityOrdersController : OrdersController<Vanity> { }
[Route("api/Orders/Existing", Name = "ExistingLink")]
public class ExistingOrdersController : OrdersController<Existing> { }
然而,尽管这似乎有效,但它并没有真正回答我的问题。我想直接在运行时使用我的通用控制器,而不是通过手动编码间接地在编译时使用它。从根本上讲,这意味着我需要ASP.NET Core能够“看到”或“发现”我的通用控制器,尽管其运行时反射名称没有以预期的“Controller”后缀结尾。