【问题标题】:Attribute routing and inheritance属性路由和继承
【发布时间】:2013-11-28 03:13:45
【问题描述】:

我正在考虑使用通用存储库为我的 API 控制器提供基本 CRUD 方法的基本控制器的想法,这样我就不必在每个新控制器中复制相同的基本代码。但是当路由属性在基本控制器中时,我遇到了识别路由属性的问题。为了准确显示我遇到的问题,我创建了一个非常简单的 WebAPI 控制器。

当我在主控制器中有一个 Get 方法并且它直接从 ApiController 继承时,我没有任何问题,并且按预期工作。

[RoutePrefix("admin/test")]
public class TestController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

当我将 Get 方法移动到基本控制器中时,它会返回 404 页面的内容。

[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{

}

public class TestBaseController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

一些更有趣的笔记:

  • 我可以通过 GET /Test/1 访问该操作。所以它仍然根据默认路由找到它。

  • 当我尝试访问 POST /admin/test 时,它返回以下 JSON

    { "Message":"没有找到与请求 URI 'http://test.com/admin/test'匹配的 HTTP 资源。", "MessageDetail":"找不到与名为 'admin' 的控制器匹配的类型。" }

有谁知道让路由使用基本控制器的属性的方法?

【问题讨论】:

  • 我正在尝试完成完全相同的事情 - 使用基本 api 控制器来处理 CRUD - 我很惊讶它并不常见。这是我第一次看到有人提到尝试这样做,而我现在陷入了路由继承问题。你能分享一下你最终做了什么吗?
  • 我让基本控制器具有实现 CRUD 操作的受保护方法,然后当我创建一个新控制器时,我必须创建公共方法并让它调用受保护方法。这样,我可以将路由放在控制器中的方法上,而不是放在基本控制器中,而不必到处复制代码。我不喜欢它,但替代方法是不使用 Route 属性,由于各种原因,这在我的情况下不起作用。
  • 感谢您的回复。我使用了类似的方法 - 只需调用 base.[Get|Post|Put|Delete]() 并传递参数。这并不理想,但我认为好处是对于不熟悉将自定义 CRUD 操作放在何处的代码的人来说更明显。您是否在网上找到任何其他讨论使用基本控制器处理 CRUD 操作的资源?我的搜索没有结果。
  • 如果您希望他们向 AttributeRouting 添加继承,请为我打开的问题投票 - aspnetwebstack.codeplex.com/workitem/1688

标签: asp.net-web-api asp.net-web-api-routing


【解决方案1】:

属性路由不能被继承。这是一个深思熟虑的设计决定。我们感觉不对,也没有看到继承它们有意义的有效场景。

你能否给出一个更现实的场景来说明你想在哪里使用它?

[更新(2014 年 3 月 24 日)]
在即将发布的 MVC Web API 5.2 版本中,将有一个名为 System.Web.Http.Routing.IDirectRouteProvider 的扩展点,您可以通过它启用您在此处寻找的继承方案。您可以使用最新的夜间构建自己尝试此操作(有关如何使用夜间构建的文档是 here

[更新(2014 年 7 月 31 日)]
在 Web API 2.2 版本中如何做到这一点的示例:

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

//---------

public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        // inherit route attributes decorated on base class controller's actions
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}

【讨论】:

  • 这就是我要玩的东西。我有一个通用存储库,并打算创建一个 RepositoryBaseApiController 来加载一个 IRepository 以用于基本的 CRUD 操作。这样,如果我需要为实体提供一个真正简单的 WebAPI,我可以创建一个 SomeEntityController 并从 RepositoryBaseApiController 继承,它会为我处理所有基础知识。我不必每次都创建操作、验证检查和路由。我会在主控制器上使用 RoutePrefix() 但基本控制器将具有相对 Route
  • 感谢您的方案。我们考虑过这种情况,但在某些情况下,问题可能是用户在基本控制器中的操作具有非常特定的路由模板,在这种情况下,将其继承到子类是不正确的......
  • 我有一堆处理文件转换器的控制器。每个都有与上传文件有关的共同端点,我看到的没有继承的唯一选择是在动作中立即重复代码或委托给助手,非常难看。 AttributeRouting 支持这个为什么原生实现不github.com/mccalltd/AttributeRouting/wiki/…
  • 我正在尝试做同样的事情 - 有一个基本的 CRUD 控制器,它使用通用服务接口处理所有基本的 crud 任务。这是一个非常真实的场景。您对用户具有“在基本控制器中的操作的特定路由模板”的响应很容易解决 - 不要从该基本控制器继承。您没有从这个设计决策中获得任何好处,并且阻止了一些非常好的功能。对于这里的每条评论,我相信还有数百人正在尝试做同样的事情。你们在这里犯了一个错误。请修复它。
  • 谢谢,基兰。我打开了一个问题。任何希望 AttributeRouting 继承的人都应该投票 - aspnetwebstack.codeplex.com/workitem/1688
【解决方案2】:

使用 Web API 2.2,您可以:

public class BaseController : ApiController
{
    [Route("{id:int}")]
    public string Get(int id)
    {
        return "Success:" + id;
    }
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}

如此处所述:http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22

【讨论】:

    【解决方案3】:

    知道了。

    [Route("api/baseuploader/{action}")]
    public abstract class BaseUploaderController : ApiController
    {
        [HttpGet] 
        public string UploadFile() 
        {
            return "UploadFile";
        }
    }
    
    
    [Route("api/values/{action}")]
    public class ValuesController : BaseUploaderController
    {
        [HttpGet]
        public string Get(int id)
        {
            return "value";
        }
    }
    

    这里需要注意的是,路由动作参数必须与动作名称相同。我找不到解决这个问题的方法。 (不能用 RouteAttribute 重命名路由)

    【讨论】:

    • 是的 api/values/upload 文件将给出“上传文件”。是的,它只是因为 {action} 直接映射到方法名称才有效。我无法让它与 RouteAttribute 一起使用,但如果你这样做了,请回发! :)
    猜你喜欢
    • 1970-01-01
    • 2019-02-19
    • 2014-06-14
    • 2014-09-17
    • 1970-01-01
    • 2023-04-02
    • 2010-09-19
    • 1970-01-01
    • 2016-11-05
    相关资源
    最近更新 更多