使用属性路由时不同的 Web API 2 个控制器

Different Web API 2 controllers when using attribute routing

我正在使用 ASP.NET Web API 为 RESTful API 构建原型 2. 简单地假设我有三个实体:客户、许可证和用户。每个客户都有一组许可证和用户。在我看来,资源 URI 在语义上应该如下所示:

myurl/api/customers for accessing all customers
myurl/api/customers/{cid} for accessing the customer {cid}
myurl/api/customers/{cid}/licences for accessing all licences of customer {cid}
myurl/api/customers/{cid}/licences/{lid} for accessing the licence {lid} of customer {cid}

用户也是如此。预期的语义允许例如两个用户拥有相同的 ID,如果他们属于不同的客户。除了许可证实体(尚未最终决定)之外,每个客户都将拥有一个专用数据库,因此在该域和资源路径中没有重叠

myurl/api/users

只有“从所有客户的数据库中加入所有用户表”的方式才有意义。

使用属性路由这个设置很容易实现。但是,所有方法都必须在同一个控制器中实现,因为来自不同控制器的方法不能共享相同的前缀 AFAIK。

实际应用程序将包含比三个实体更多的实体,因此我预计控制器的实现会变得相当庞大。我现在的问题是,如何将方法拆分到不同的控制器中?我考虑过使用一个主控制器,它只是将要完成的工作分派给另一个控制器。例如

[Route("{customer:int}/licences/{licence:int}")]
public HttpResponseMessage GetLicence(int customer, int licence)
{
    // pretend the called method is static
    return LicenceController.GetLicence(customer, licence);
}

但是,我不知道如何正确实施:我应该为每次调用创建一个新的 LicenceController 吗?或者有一个这种类型的 属性 并调用它的方法?实际上实现了一些静态方法?

另一个缺点是这会在选择器和执行控制器之间引入硬编码依赖关系class我觉得这不是一个干净的解决方案。

我想出了一个解决方法,它使用这样的资源路径:

myurl/api/licences/customer-{cid} for accessing all licences of customer {cid}
myurl/api/licences/customer-{cid}/{lid} for accessing the licence {lid} of customer {cid}

这工作得很好,但弄乱了 IMO 的同类语义。我知道我可以编写一个自定义选择器 class 但这似乎需要相当多的工作才能使其正确。

所以我的问题是,将处理传入 HTTP 消息的代码拆分到单独的控制器中以便松散耦合并且资源语义一致的最佳(也许是最有效)方法是什么?

你会有两个控制器。一份给 return 客户,一份给 return 许可证。对于客户,不需要使用属性,因为默认值很好:

public class CustomersController : ApiController
{
    // GET: api/Customers
    public IEnumerable<Customer> Get()
    {
        return new List<Customer> 
        {
            new Customer { Id = 1, Name = "Wayne" },
            new Customer { Id = 2, Name = "John" }
        };
    }

    // GET: api/Customers/5
    public Customer Get(int id)
    {
        return new Customer { Id = 1, Name = "Wayne" };
    }
}

然后你可以在控制器上添加 RoutePrefix 属性来为 api/Customers/1/licences 添加,其余的可以由 Route 在动作上处理。我将控制器命名为 CustomerLicencesController,因为您可能希望有一个许可证控制器来获取特定许可证或所有许可证,例如 api/licencesapi/licences/1.

[RoutePrefix("api/customers/{customer}/licences")]
public class CustomerLicencesController : ApiController
{
    // GET: api/Customers/1/licences
    [Route("")]
    public IEnumerable<Licence> Get(int customer)
    {
        return new List<Licence>
        {
            new Licence { Id = 1, Name = "Test" },
            new Licence { Id = 2, Name = "Test2" }
        };
    }

    // GET: api/Customers/1/licences/1
    [Route("{id}")]
    public Licence Get(int customer, int id)
    {
        return new Licence { Id = 1, Name = "Test" };
    }
}

有关路由属性的更多信息,请查看 this