【问题标题】:Inheriting route attribute in .NET Core Web API在 .NET Core Web API 中继承路由属性
【发布时间】:2020-06-13 17:54:59
【问题描述】:

假设我将制作以下 REST API:

https://somedomain.com/api/Companies/{cid}

https://somedomain.com/api/Companies/{cid}/Employees/{eid}

https://somedomain.com/api/Companies/{cid}/Products/{pid}

在上述情况下,我需要路由(Route() 属性)可扩展,以便形成路由层次结构。我尝试了不同的方法,但它们似乎都不像样。任何人都可以就我如何实现routing inheritance 给我一些建议...

    [Route("api/CompaniesController/{cid}")]
    [ApiController]
    public class CompaniesController : ControllerBase
    {
    }

    [Route("Employees/{eid}")]
    public class EmployeesController: CompaniesController 
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }

    [Route("Products/{pid}")]
    public class ProductsController: CompaniesController 
    {
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }

【问题讨论】:

    标签: c# .net-core routes asp.net-core-mvc asp.net-core-webapi


    【解决方案1】:

    你不应该为此使用不同的控制器,你应该在同一个控制器中使用动作,所以动作可以“继承”控制器路由。

    我把你的例子改成了这样:

    [Route("api/Companies/{cid}")]
    [ApiController]
    public class CompaniesController : ControllerBase
    {
    }
    
    [HttpGet]
    [Route("Employees/{eid}")]
    public IEnumerable<string> GetEmployees(string cid, string eid)
    {
        return new string[] { "value1", "value2" };
    }
    
    [HttpGet]
    [Route("Products/{pid}")]
    public IEnumerable<string> GetCompanies(string cid, string pid)
    {
        return new string[] { "value1", "value2" };
    }
    

    或者,如果您想保留完整路线作为示例,但想要单独的控制器(以避免单个控制器执行数十个操作),您可以使用像这样的单独控制器,只需在每个控制器上添加您想要的完整路线控制器:

    [Route("api/Companies/{cid}")]
    [ApiController]
    public class CompaniesController : ControllerBase
    {
    }
    
    [Route("api/Companies/{cid}/Employees/{eid}")]
    public class EmployeesController: ControllerBase
    {
        [HttpGet]
        public IEnumerable<string> Get(string cid, string eid)
        {
            return new string[] { "value1", "value2" };
        }
    }
    
    [Route("api/Companies/{cid}/Products/{pid}")]
    public class ProductsController: ControllerBase 
    {
        [HttpGet]
        public IEnumerable<string> Get(string cid, string pid)
        {
            return new string[] { "value1", "value2" };
        }
    }
    
    [Route("api/Companies/{cid}/Employee/{eid}/Privilege/{pid}/Account/{aid}")]
    public class AccountsController: ControllerBase 
    {
        [HttpGet]
        public IEnumerable<string> Get(string cid, string eid, string pid, string aid)
        {
            return new string[] { "value1", "value2" };
        }
    }
    

    因此,您只需在控制器路由中添加完整路径,而不是继承控制器。

    如果继承控制器导致路由也被继承,那将来可能会出现问题。想象有人改变一个控制器来继承一个新的控制器(从而引入一条新的路由到 url)。这不仅会影响您正在更改的控制器,还会影响所有子控制器。

    在我看来,最好不要有可能影响路线外观的外部因素。只有控制器内部的 [Route] 可以更改它。它更简单、更容易找到受影响的内容、更易于维护、更明确,继承或外部代码不会隐藏任何内容。

    问题是:你真的需要路线中的完整路径吗?为什么不改用这个:

    • api/Companies/{cid}
    • api/Employees/{eid}
    • api/Products/{pid}
    • api/Accounts/{aid}

    这是你应该做出的决定。

    作为旁注,您可以考虑返回 IActionResult 而不是实际的对象。此外,您可能希望在参数中使用[FromRoute]。最后,我在操作方法名称中使用了复数,因为它返回一个数组,但如果您在路由中提供 id,则表明您返回的是单个实体,而不是数组。

    我鼓励你阅读ASP.NET Core Attribute Routing

    【讨论】:

    • 谢谢@Alisson,这意味着我应该只有一个控制器,即使它可能有这么多子资源?例如公司 -> 员工 -> 特权 -> 帐户。如果是这样的话,这个控制器类最终可能会包含很多很多来自不同资源的方法。 :(
    • @mannok 这更像是您应该做出的设计决定。您真的需要路线中的整个路径吗?例如“公司/{cid}/employee/{eid}/privilege/{pid}/account/{aid}”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 2019-08-06
    • 1970-01-01
    • 2013-11-28
    • 1970-01-01
    • 2017-03-24
    • 2019-10-30
    相关资源
    最近更新 更多