Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 消除RESTful服务的歧义_C#_Asp.net Mvc 3_Rest_Asp.net Mvc Routing_Restful Url - Fatal编程技术网

C# 消除RESTful服务的歧义

C# 消除RESTful服务的歧义,c#,asp.net-mvc-3,rest,asp.net-mvc-routing,restful-url,C#,Asp.net Mvc 3,Rest,Asp.net Mvc Routing,Restful Url,我在以前的项目中广泛使用了WCF。最近,我一直在探索如何使用ASP.NETWebAPI创建RESTful服务。在研究了RESTful服务的使用方法和不使用方法,甚至在实践中进行了尝试之后,我有一个非常简单的问题 假设我有一个userscoontroller(继承ApiController),其中我需要有3个GET-type操作方法: GetUsers() GetUserById(string id) GetUserByName(string name) 假设我在Web

我在以前的项目中广泛使用了WCF。最近,我一直在探索如何使用ASP.NETWebAPI创建RESTful服务。在研究了RESTful服务的使用方法和不使用方法,甚至在实践中进行了尝试之后,我有一个非常简单的问题

假设我有一个
userscoontroller
(继承
ApiController
),其中我需要有3个GET-type操作方法:


    GetUsers()
    GetUserById(string id)
    GetUserByName(string name)
假设我在WebApiConfig中也有以下路由

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

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

我想要

http://localhost:/api/users/5c6fe209-821e-475f-920d-1af0f3f52a82
调用
GetUserById(字符串id)

http://localhost:/api/users/jdoe
调用
GetUserByName(字符串名)

相反,我希望发生的是,我要么会得到一个错误,要么对这两种情况都只调用第一个操作方法


由于在路由上引入消除歧义的操作被认为是对纯RESTful服务的一种偏离,因此如何使不同的URL调用相应的操作方法?我浏览过web,大多数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的约束对象以正则表达式的形式为不同的URL参数指定约束。在本例中,对于ById路由,我们仅在id参数为数字时匹配。只有当name参数是一个字母字符字符串时,我们才匹配ByName。我们在没有参数的情况下匹配所有参数。注意,每个正则表达式上的“+”不指定空值。路由是按照定义的顺序匹配的,因此您应该将最不特定的规则放在更特定的规则下面

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


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

谢谢Steve。试想一下,如果你这样做,你会不会牺牲RESTful服务的流畅性?事实上,如果它变得如此复杂,强迫调用方提供动作名称不是更好或更容易吗?2个月后。。。我个人认为这并不复杂。但要回答这个问题,使用带有隐式筛选操作的服务更简单,但编写服务器端服务更困难。如果您选择另一种方法,您只是将复杂性转移到客户机上。我要说的是,如果您有一个用户名是数字的用户(在本例中不太可能,但对于其他实体可能),您的名称将被解释为id,因此要求显式指定筛选器方法可能是最好的方法。实际上,最剩余的方法可能是使用/users/{id}方法,但在按名称搜索用户时,将使用/users/?name={name}。这在某种程度上是假设名称不是唯一的。我想我要说的是,您在URL中输入的任何内容都应该唯一地标识该实体,而不是唯一的任何内容都应该是集合中的查询参数。用户可能是两个属性(姓名和id)都是唯一的情况下比较少见的例子之一,在这种情况下,我会和自己在圈子里争论,直到我不在乎了,到家了。
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", }
);