消除RESTful服务的歧义

本文关键字:歧义 服务 RESTful 消除 | 更新日期: 2023-09-27 17:58:03

我在以前的项目中广泛使用了WCF。最近,我一直在探索在创建RESTful服务时使用ASP.NET Web API。在研究了RESTful服务的DO和DONT,甚至在实践中进行了尝试之后,我有一个相当简单的问题。

假设我有一个UsersController(继承ApiController),其中我需要有3个GET类型的操作方法:


    GetUsers()
    GetUserById(string id)
    GetUserByName(string name)

假设我在WebApiConfig中也有以下路径


    config.Routes.MapHttpRoute(
        name: "Users",
        routeTemplate: "api/users/{id}",
        defaults: new { controller = "users", id = RouteParameter.Optional }
    );

http://localhost:<port>/api/users显然会调用GetUsers()

当我需要调用采用单个参数的两个操作方法中的任何一个时,问题就出现了。

我想要

http://localhost:<port>/api/users/5c6fe209-821e-475f-920d-1af0f3f52a82调用GetUserById(string id)

http://localhost:<port>/api/users/jdoe调用GetUserByName(string name)

相反,我预计会发生的情况是,无论哪种情况,我要么会得到一个错误,要么只调用第一个操作方法。

由于在路由上引入消除歧义的操作被认为是对纯RESTful服务的偏离,我如何使不同的URL调用相应的操作方法?我在网上搜索过,大多数RESTful服务的例子(由纯粹主义者)都停留在第一个操作方法来检索所有内容,第二个操作方法检索单个项目。

消除RESTful服务的歧义

这可以使用更具体的受限路由来实现。下面的例子与您试图实现的目标类似:

config.Routes.MapHttpRoute(
    name: "ById",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "GetById" },
    constraints: new { id = @"'d+" }
);
config.Routes.MapHttpRoute(
    name: "ByName",
    routeTemplate: "api/{controller}/{name}",
    defaults: new { action = "GetByName", },
    constraints: new { name = @"'w+" }
);
config.Routes.MapHttpRoute(
    name: "All",
    routeTemplate: "api/{controller}",
    defaults: new { action = "GetAll", }
);

传递给MapHttpRoute的constraints对象以正则表达式的形式为不同的URL参数指定约束。在本例中,对于ById路由,只有当id参数为数字时,我们才会匹配。只有当name参数是一个由alpha字符组成的字符串时,我们才匹配ByName。我们无参数匹配All。请注意,每个正则表达式上的"+"不指定空值。路由是按照定义的顺序匹配的,所以你应该把最不具体的规则放在更具体的规则下面。

在您的情况下,您需要找到或编写一个与您正在使用的GUID格式匹配的正则表达式,并约束ById路由以匹配此表达式。然后,您需要在下面定义接受任何字符串的ByName路由。因为它在下面,所以只有当输入字符串不是GUID时才会调用它。

我还应该补充一点,如果你以前没有使用过MVC路由,它们是非常具体的。您会注意到,我的参数名称为ById的{id}和ByName的{name}。这些参数与控制器方法上输入参数的确切名称匹配是很重要的,因为路由器构建方法调用就是使用这个名称的。即使一个操作只有一个参数,如果名称映射不正确,也会出现错误。