接口上定义的 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_StartWebApiConfig.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 属性路由的主要内容,如果未能解决你的问题,请参考以下文章

WebApi-路由机制

Mvc-WebAPI特性路由(自定义路由)Demo

web api - 消息处理程序属性路由

对于某些方法,基于属性的webapi2路由返回404

Sitecore 7.5 的 MVC 检测和 WebApi 属性路由

.Net Framework 4.6.1 WebApi,属性路由和默认路由不起作用