我提前道歉,这篇文章与您的要求有些偏离,但是当我读到您的问题时,所有这些都浮现出来了。
WebAPI 匹配语义
(thedefault路由)WebAPI 相当简单。
- 它将操作名称与动词相匹配(动词 = GET?查找以“get”开头的方法名称)
- 如果传递了参数,则 api 会寻找带有参数的操作
因此,在您的代码示例中,不带参数的 GET 请求与Get*( )
不带参数的函数。获取包含和 ID 查找Get***(int id)
.
Examples
虽然匹配语义很简单,但它给 MVC 开发人员(至少是这个开发人员)带来了一些困惑。让我们看一些例子:
奇怪的名字- 你的 get 方法可以命名为任何名称,只要它以“get”开头即可。因此,对于小部件控制器,您可以命名您的函数GetStrawberry()
并且仍然会匹配。将匹配视为类似:methodname.StartsWith("Get")
多种匹配方式- 如果您有两个不带参数的 Get 方法,会发生什么情况?GetStrawberry()
and GetOrange()
。据我所知,代码中首先定义的函数(文件顶部)获胜......奇怪。这会产生副作用,使控制器中的某些方法无法访问(至少对于默认路由)......陌生人。
NOTE:测试版的行为与上面的“匹配多种方法”相同 - RC 和发布版本有点强迫症。如果存在多个潜在匹配项,它会引发错误。此更改消除了多个不明确匹配的混乱。同时,它降低了我们在同一控制器中混合 REST 和 RPC 风格接口的能力,依赖于顺序和重叠路由。
该怎么办?
嗯,WebAPI 是新事物,共识仍在凝聚。社区似乎在很大程度上遵循 REST 原则。然而,并不是每个 API 都可以或应该是 RESTful,有些 API 更自然地以 RPC 风格表达。 REST & 人们所说的 REST 似乎是相当多 混乱的, well 至少到 罗伊·菲尔丁.
作为一个实用主义者,我怀疑许多 API 将是 70% RESTful,带有少量 RPC 风格的方法。首先,仅控制器的激增(考虑到 webapi 绑定方法)就会让开发人员发疯。其次,WebAPI 并没有真正的内置方法来创建 api 路径的嵌套结构(意味着:/api/controller/
很容易,但是/api/CATEGORY/Sub-Category/Controller
是可行的,但很痛苦)。
从我的角度来看,我希望看到 webAPI 文件夹结构控制默认 API 路径...这意味着如果我在 UI 项目中创建一个类别文件夹,那么/api/Category
将是默认路径(与这篇 MVC 文章平行).
我做了什么?
所以,我有一些要求:(1)在大多数情况下能够使用restful语法,(2)控制器有一些“命名空间”分离(认为子文件夹),(3)能够调用额外的rpc-必要时喜欢方法。实现这些要求取决于巧妙的路由。
// SEE NOTE AT END ABOUT DataToken change from RC to RTM
Route r;
r = routes.MapHttpRoute( name : "Category1",
routeTemplate : "api/Category1/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category1"};
r = routes.MapHttpRoute( name : "Category2",
routeTemplate : "api/Category2/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category2"};
routes.MapHttpRoute( name : "ApiAllowingBL",
routeTemplate : "api/{controller}/{action}/{id}",
defaults : new { id = RouteParameter.Optional } );
routes.MapHttpRoute( name : "DefaultApi",
routeTemplate : "api/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
- 前两条路线创建“子文件夹”路线。我需要为每个子文件夹创建一条路径,但我将自己限制在主要类别中,因此最终只创建了 3-10 个子文件夹。注意这些路由如何添加
Namespace
数据令牌,以限制为特定路由搜索哪些类。当您向 UI 项目添加文件夹时,这与典型的命名空间设置非常对应。
- 第三条路线允许调用特定的方法名称(如传统的 mvc)。由于 Web API 取消了 URL 中的操作名称,因此可以相对容易地判断哪些调用需要此路由。
- 最后一个路由条目是默认的 Web api 路由。这会捕获任何类,特别是我的“子文件夹”之外的类。
另一种说法
我的解决方案归结为进一步分离控制器/api/XXXX
并没有变得太拥挤。
- 我在我的 UI 项目中创建一个文件夹(可以说
Category1
),并将 api 控制器放入该文件夹中。
- Visual Studio 自然地根据文件夹设置类命名空间。所以
Widget1
in the Category1
文件夹的默认命名空间为UI.Category1.Widget1
.
- 当然,我希望 api URL 能够反映文件夹结构(
/api/Category1/Widget
)。您在上面看到的第一个映射通过硬编码实现了这一点/api/Category1
进入路线,然后namespace
令牌限制将搜索匹配控制器的类。
NOTE:截至发布时DataTokens
默认为空。我不是
确定如果这是一个错误,或者一个功能。所以我写了一个小助手
方法并添加到我的RouteConfig.cs
文件....
r.AddRouteToken("Namespaces", new string[] {"UI.Controllers.Category1"});
private static Route AddRouteToken(this Route r, string key, string[] values) {
//change from RC to RTM ...datatokens is null
if (r.DataTokens == null) {
r.DataTokens = new RouteValueDictionary();
}
r.DataTokens[key] = values;
return r;
}
NOTE 2:即使认为这是一篇 WebAPI 1 帖子,正如 @Jamie_Ide 在评论中指出的那样,上述解决方案在 WebAPI 2 中不起作用,因为IHttpRoute.DataTokens
没有二传手。为了解决这个问题,你可以使用一个简单的扩展方法,如下所示:
private static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, string[] namespaceTokens)
{
HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults);
HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints);
IDictionary<string, object> tokens = new Dictionary<string, object>();
tokens.Add("Namespaces", namespaceTokens);
IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: tokens, handler:null);
routes.Add(name, route);
return route;
}