MVC WebApi中的方法如何映射到http动词?

And*_*ltz 31 asp.net-mvc asp.net-mvc-routing asp.net-web-api

在以下链接的5分钟视频中,在1:10标记处,Jon Galloway说,在他的CommentsController控制器类中添加一个名为DeleteComment的方法将按照约定自动映射到删除http动词.

如何使用WebApi的MVC知道如何将方法路由到正确的动词?我知道global.asax.cs文件中的路由将请求路由到正确的控制器,但是删除请求如何"按惯例"映射到delete方法或任何方法?特别是当每个动词可以有多于1种方法时?"按照惯例"让我觉得它只是在查看方法名称中的第一个单词...但如果是这样的话,它必须读取方法的签名来告诉两个删除方法或两个方法分开...以及是这个定义了吗?

视频:http: //www.asp.net/web-api/videos/getting-started/delete-and-update

谢谢!

编辑:以下是WebApi模板中的示例ValuesController类中的代码.这是我原来问题的来源.区分这些(以及控制器中的任何其他方法)的"约定"如何工作?

// GET /api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET /api/values/5
    public string Get(int id)
    {
        return value;
    }
Run Code Online (Sandbox Code Playgroud)

EBa*_*arr 59

我提前道歉,这个帖子偏离了你的问题,但当我读到你的问题时,所有这些都冒出来了.

WebAPI匹配语义WebAPI中
(默认路由)使用的匹配语义非常简单.

  1. 它将动作的名称与动词匹配(动词= GET?查找以"get"开头的方法名称)
  2. 如果传递参数,则api使用参数寻找动作

因此,在您的代码示例中,没有参数的GET请求与没有参数的Get*( )函数匹配.Get包含和ID查找Get***(int id).

示例
虽然匹配语义很简单,但它会给MVC开发人员带来一些困惑(至少对于这个开发人员而言).让我们看一些例子:

奇数名称 - 您的get方法可以命名为任何名称,只要它以"get"开头即可.因此,在小部件控制器的情况下,您可以命名您的功能GetStrawberry(),它仍然会匹配.将匹配视为: methodname.StartsWith("Get")

多个匹配方法 - 如果您有两个没有参数的Get方法会发生什么? GetStrawberry()GetOrange().我可以告诉你,代码中首先定义的函数(文件的顶部)赢得了...奇怪.这会产生副作用,使控制器中的某些方法无法访问(至少使用默认路由)....陌生人.

注意:测试版的行为如上所述'匹配多种方法' - RC和Release版本更多OCD.如果存在多个潜在匹配,则会引发错误.此更改消除了多个模糊匹配的混淆.同时,它降低了我们在同一控制器中混合REST和RPC样式接口的能力,依赖于顺序和重叠路由.

该怎么办?
嗯,WebAPI是新的,共识仍然是合并的.社区似乎很多时候都在寻求REST原则.然而,并非每个API都可以或应该是RESTful,有些更自然地以RPC样式表达.REST与人称之为REST似乎是源相当多的 混乱,以及 至少 罗伊菲尔丁.

作为一个实用主义者,我怀疑许多API将是70%RESTful,并且有一些RPC样式方法.首先,单独的控制器扩散(鉴于webapi绑定方法)将驱动开发人员疯狂.其次,WebAPI实际上没有内置的方法来创建api路径的嵌套结构(意思是: /api/controller/很容易,但是/api/CATEGORY/Sub-Category/Controller可行,但很痛苦).

从我的角度来看,我希望看到webAPI文件夹结构控制默认的API路径......这意味着如果我在我的UI项目中创建一个Category文件夹,那么/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 } );
Run Code Online (Sandbox Code Playgroud)
  • 前两个路由创建"子文件夹"路由.我需要为每个子文件夹创建一个路由,但是我将自己局限于主要类别,所以我最终只有3-10个.注意这些路由如何添加Namespace数据令牌,以限制为特定路由搜索的类.当您将文件夹添加到UI项目时,这与典型的命名空间设置很好地对应.
  • 第三条路径允许调用特定的方法名称(如传统的mvc).由于Web API消除了URL中的操作名称,因此可以相对容易地判断哪些调用需要此路由.
  • 最后一个路由条目是默认的Web api路由.这会捕获任何类,特别是我的"子文件夹"之外的类.

说另一种方式
我的解决方案归结为分离控制器更多,所以/api/XXXX没有太拥挤.

  • 我在我的UI项目中创建了一个文件夹(比如说Category1),并将api控制器放在该文件夹中.
  • Visual Studio自然地根据文件夹设置类名称空间.所以Widget1在该Category1文件夹中获取默认名称空间UI.Category1.Widget1.
  • 当然,我希望api URL能够反映文件夹结构(/api/Category1/Widget).您在上面看到的第一个映射通过硬编码/api/Category1到路由中来实现,然后namespace令牌限制将搜索匹配控制器的类.

注意:DataTokens默认情况下,发布时为null.我不确定这是一个错误还是一个功能.所以我写了一个小帮手方法并添加到我的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;
}
Run Code Online (Sandbox Code Playgroud)

注2:甚至认为这是一篇WebAPI 1帖子,正如@Jamie_Ide在评论中指出的那样,上述解决方案在WebAPI 2中不起作用,因为IHttpRoute.DataTokens没有setter.要解决这个问题,您可以使用这样的简单扩展方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 在Web Api 2中,IHttpRoute.DataTokens没有setter,因此该解决方案不再有效.我开始明白Web Api真的不想适应这种路由风格.您始终可以将控制器组织到文件夹中,并且可以定义访问它们的路由,但是仍然可以通过默认路由访问它们,重复的名称将引发运行时异常. (3认同)