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

时间:2012-05-03 21:43:53

标签: asp.net-mvc asp.net-mvc-routing asp.net-web-api

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

MVC与WebApi如何知道如何将方法路由到正确的动词?我知道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;
    }

2 个答案:

答案 0 :(得分:59)

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

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

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

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

    <强>实施例
    虽然匹配的语义很简单,但它给MVC开发人员(至少是这个开发人员)造成了一些困惑。让我们看一些例子:

    奇数名称 - 您的get方法可以命名为任何名称,只要它以&#34; get&#34;开头即可。因此,对于窗口小部件控制器,您可以将函数命名为GetStrawberry(),它仍将匹配。将匹配视为:methodname.StartsWith("Get")

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

      

    注意:测试版表现如上,以便匹配多种方法&#39; - RC&amp;发布版本有点强迫症。如果存在多个潜在匹配,则会引发错误。此更改消除了多个模糊匹配的混淆。同时,它降低了我们在同一个控制器中混合REST和RPC样式接口的能力,依赖于顺序&amp;重叠的路线。

    怎么办?
    嗯,WebAPI是新的,共识仍然是合并的。社区似乎很多时候都在寻求REST原则。然而,并非每个API都可以或应该是RESTful,有些更自然地以RPC样式表达。 REST&amp;人们称之为REST似乎是quite a bit of confusionwell at least to Roy Fielding的来源。

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

    从我的角度来看,我希望看到webAPI文件夹结构控制默认的API路径......这意味着如果我在我的UI项目中创建一个Category文件夹,那么/api/Category将成为默认路径(// 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}})。

    我做了什么?
    所以,我有一些要求:(1)在大多数情况下能够使用restful语法,(2)有一些&#34;命名空间&#34;控制器分离(想想子文件夹),(3)能够在必要时调用其他类似rpc的方法。实现这些要求归结为巧妙的路由。

    Namespace
    • 前两条路线创建&#34;子文件夹&#34;路线。我需要为每个子文件夹创建一个路由,但是我将自己局限于主要类别,所以我最终只有3-10个。请注意这些路由如何添加/api/XXXX数据令牌,以限制为特定路由搜索的类。当您将文件夹添加到UI项目时,这与典型的命名空间设置很好地对应。
    • 第三条路径允许调用特定的方法名称(如传统的mvc)。由于Web API消除了URL中的操作名称,因此相对容易判断哪些调用需要此路由。
    • 最后一个路由条目是默认的Web api路由。这会抓住任何课程,尤其是我的子文件夹以外的课程。

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

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

    注意:自版本RouteConfig.cs起,默认为null。我不是   确定parallel to this MVC article。所以我写了一个小帮手   方法并添加到我的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; } 文件....

    IHttpRoute.DataTokens
      

    注意2 :甚至认为这是一篇WebAPI 1帖子,正如@Jamie_Ide在评论中指出的那样,上述解决方案在WebAPI 2中不起作用,因为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; } 没有二传手。要解决这个问题,您可以使用这样的简单扩展方法:

    {{1}}

答案 1 :(得分:3)

经常出现这种情况。对此有不同的看法。我个人目前还没有订阅任何特定的想法,但似乎是每个资源一个控制器的一个在REST社区中最受欢迎。

所以基本上你可以:

  1. 在您的路径中公开操作(Web API将单词action视为与MVC类似),但通常不打算使用。
  2. 根据此post
  3. 定义具有不同参数的方法
  4. 正如我所说,大多数建议使用每个资源一个控制器。因此,即使在Web API示例中,实体集合的控制器对于实体本身的控制器也是不同的。请阅读Rob Conery的posthere是格伦的回答。