接口上定义的 WebApi 属性路由
Posted
技术标签:
【中文标题】接口上定义的 WebApi 属性路由【英文标题】:WebApi attribute routing defined on interface 【发布时间】:2014-05-02 01:03:51 【问题描述】:我正在尝试将 WCF/REST 服务项目转换为 MVC/WebAPI。服务层作为不同终端系统的包装器被多次实现,所有实现都遵循接口中定义的通用契约 (IContract
)。使用 WCF,我们在作为 Web 服务方法公开的每个方法上定义了 [WebInvoke]
和 [OperationContract]
属性。在 WebAPI 中,这可以通过在控制器上定义的属性路由来简化。但是,我想保留接口上定义的路由属性,因此所有实现的行为都相似。
这是旧界面的示例:
[ServiceContract]
public interface IContract
[WebInvoke(UriTemplate = "Version", Method = "GET", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
string GetVersion();
这是我希望得到的工作:
public interface IContract
[Route("Version")]
[HttpGet]
string GetVersion();
我也会考虑创建一个抽象基类,但是另一个*** question 让我觉得没有合适的替代品来替代[WebInvoke(UriTemplate)]
使用WebAPI 属性路由。是这样吗,或者有人可以向我指出类似的、受支持的技术吗?
谢谢
【问题讨论】:
【参考方案1】:在对 ASP.NET 团队的成果进行更多研究后,看起来System.Web.Http.Routing.IDirectRouteProvider
可扩展性有望满足我们的需求。在撰写本文时,这仅在使用 ASP.NET 夜间构建时可用,但有望在我们准备推出时达到 RTM 或至少 CTP。
下面的代码展示了如何为我上面的示例实现这一点(原始来源:https://aspnetwebstack.codeplex.com/workitem/1464)
在Application_Start
或WebApiConfig.Register()
中,将新的IDirectRouteProvider
传递给MapHttpAttributeRoutes()
方法。
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
如下定义路由提供者。这基本上只是打开属性继承,因此它将从基类(但不是接口)读取路由。
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
protected override IReadOnlyCollection<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
在我的例子中,我们创建了一个抽象的 BaseApiController,它为 WebAPI 方法提供[Route]
属性。
public abstract class BaseApiController : ApiController, IContract
[Route("Version")]
[HttpGet]
public abstract string GetVersion();
我们的终端系统以前是实现 IContract 接口的 WCF REST 服务端点。对于 WebAPI,控制器现在派生自 BaseApiController。 (我们还必须将名称从 ContractService 更改为 ContractServiceController。)为了向后兼容,我们保留了 IContract,但它现在在 BaseApiController 上实现。 [RoutePrefix]
属性是在控制器上定义的,在本例中是我们的根 URL。
[RoutePrefix("")]
public class TestController : BaseApiController
public override string GetVersion();
return "Version 1.0.0.0";
【讨论】:
【参考方案2】:您不能使用Route
属性来装饰界面。这是因为 Web API 在其自己的框架内工作......它检查所有类型为 ApiController
的对象以确定路由的内容。它基本上询问您的自定义控制器方法的属性以创建路由。它不会寻找您的自定义IContract
接口,因此它不会关心进行此类路由。
即使您更改 Web API 的代码库以从接口读取和处理 Route
属性,这将如何工作?接口是一种契约,不包含功能。那么,如果它看到"Version"
的路由,它将映射到哪个控制器实现/实例?随便找的第一个可以赋值给IContract
?最后一个?他们都是?如果所有这些,你如何一次执行 N 个控制器方法?
我相信你需要评估你对属性路由的理解。它告诉 Web API 框架在 THIS 控制器上为 THIS 路由使用 THIS 方法。您无法将其抽象为接口,因为它无法确定要执行的派生类。
【讨论】:
虽然我同意这是一个完全合理的解释,但我想令人惊讶的是,WCF REST 通过使用放置在接口上的[WebInvoke(UriTemplate)]
属性支持了一种非常相似的方法。更好的结构可能是实现一个带有路由属性的抽象基类,看起来 Web API 5.2 版应该支持这种情况(参见this updated answer)
我在 WCF REST 上听到了你的声音,但我个人觉得它违反了 SOLID 原则。让接口知道实现类有点违背目的——至少在我看来。以上是关于接口上定义的 WebApi 属性路由的主要内容,如果未能解决你的问题,请参考以下文章