如何在MVC的WebAPI的方法映射到HTTP动词? [英] How does a method in MVC WebApi map to an http verb?

查看:228
本文介绍了如何在MVC的WebAPI的方法映射到HTTP动词?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下链接了5分钟的视频,在1:10大关,乔恩·加洛韦说,增加一个名为DeleteComment方法来他CommentsController控制器类将自动约定映射到HTTP删除动词。

如何使用MVC的WebAPI知道如何击溃的方法来正确的动词?我知道在的global.asax.cs文件路由请求路由到正确的控制器,但如何删除请求得到按照惯例映射来删除方法或任何方法?尤其是当有可以为每个动词超过1的方法? 按照惯例让我觉得它只是在寻找一个方法名称的第一个字......但即便如此,它必须要读取的方法签名,告诉两个删除方法或两个get方法分开......哪里在这一切的定义?

视频:
<一href=\"http://www.asp.net/web-api/videos/getting-started/delete-and-update\">http://www.asp.net/web-api/videos/getting-started/delete-and-update

谢谢!

编辑:
这里是自带的模板的WebAPI样品Values​​Controller类code。这是我原来的问题的根源。如何在公约,这些区分(和控制器的任何其他方法)可以吗?

  // GET / API /值
    公共IEnumerable的&LT;串GT;得到()
    {
        返回新的字符串[] {值1,值2};
    }    // GET / API /价值/ 5
    公共字符串GET(INT ID)
    {
        返回值;
    }


解决方案

我提前道歉,这篇文章从偏离你问了一下,但所有这一切都冒泡当我看到你的问题。

的WebAPI语义匹配结果
语义使用的匹配(即默认的路径中)的WebAPI相当简单。


  1. 它匹配与动词的动作名称(动词= GET?查找开头为get方法名称)

  2. 如果一个参数传递,该API旨在动作与参数

所以你的code样品中不带参数的GET请求获取*()匹配功能没有参数。包含和ID获取查找一个获取***(INT ID)

例子结果
而匹配的语义很简单,它创造了MVC开发一些混乱(嗯,至少这个开发者)。让我们来看一些例子:

奇名称的 - 因为它以get开头的GET方法可以命名为任何东西,这么久。因此,在小部件控制器的情况下,你可以命名你的函数 GetStrawberry(),它仍然会匹配。匹配看作是这样的: methodname.StartsWith(GET)

多个匹配的方法的 - 如果你有一个不带参数的两个Get方法会发生什么? GetStrawberry() GetOrange()。尽我可以告诉,首先定义(该文件的顶部)在code中的功能赢得...奇怪。这在你的控制器无法访问(至少默认路由)使得一些方法的副作用....陌生人。


  

注意:公测表现为上述匹配多种方法 - 该RC&放大器;发行版本是多一点强迫症。如果有多个潜在匹配它抛出一个错误。这一更改移除多暧昧匹配的混乱。与此同时,它降低了我们在同一个控制器混合REST和RPC风格的界面,依靠订单和放大器的能力;重叠的路线。


怎么办?结果
嗯,是的WebAPI新的共识仍是合并。社区似乎是深远的REST原则不少。然而,不是每个API可以或者应该是RESTful的,有些是更自然在RPC样式pssed前$ P $。 REST和放大器;人们称之为REST似乎是的颇有几分 <一个href=\"http://$c$cbetter.com/glennblock/2012/02/28/why-are-there-2-controllers-in-the-asp-net-web-api-contactmanager-example-rest-has-nothing-to-with-it-2/\">of混乱,以及at至少要 罗伊菲尔丁

作为一个实用主义者,我怀疑很多API的将是70%REST风格,与RPC样式方法一知半解。首先,单独的控制器扩散(给定的WebAPI结合法)是要疯狂驱动器开发商。其次,的WebAPI并没有真正有一个内置的方式来创建的API路径的嵌套结构(意为: / API /控制器/ 很容易,但 / API /分类/子类别/控制器是可行的,但疼痛)。

从我的角度,我想如果我创造我的UI项目类别的文件夹,然后喜欢看到的WebAPI文件夹结构控制的默认路径API ...这意味着 / API /类别将是默认路径(这是<一个href=\"http://omaralzabir.com/create_asp_net_mvc_controllers_under_namespace_and_specific_url/\">parallel这MVC文章)。

我做了什么?结果
所以,我有几个要求:(1)能够在大多数情况下,使用宁静的语法,(2)有一些命名空间控制器的分离(思子文件夹),(3)能够调用额外的中RPC类似的方法在必要的时候。落实这些要求归结为聪明的路由。

  // SEE注AT END有关从RC到RTM DataToken变化路径R;
R = routes.MapHttpRoute(名称:组别,
                         routeTemplate:API /组别/ {}控制器/(编号),
                         默认:新{ID = RouteParameter.Optional});
r.DataTokens [命名空间] =新的字符串[] {UI.Controllers.Category1};R = routes.MapHttpRoute(名称:组别,
                         routeTemplate:API /组别/ {}控制器/(编号),
                         默认:新{ID = RouteParameter.Optional});
r.DataTokens [命名空间] =新的字符串[] {UI.Controllers.Category2};routes.MapHttpRoute(名称:ApiAllowingBL
                         routeTemplate:API / {控制器} / {行动} / {ID}
                         默认:新{ID = RouteParameter.Optional});routes.MapHttpRoute(名称:DefaultApi
                         routeTemplate:API / {}控制器/(编号),
                         默认:新{ID = RouteParameter.Optional});


  • 前两个途径打造子文件夹的路线。我需要为每个子文件夹的路径,但只给自己定下大类,所以我只能用这些3-10结束。注意如何将这些路由添加命名空间数据令牌,以限制被搜索哪些类特定的路线。这很好地对应为您的文件夹添加到UI项目典型的命名空间设置。

  • 第三条道路允许特定的方法名被调用(如传统的MVC)。由于网络API中的URL摒弃了动作名称,这是比较容易判断哪些调用希望这条路线。

  • 最后一个路由条目是默认Web API的路线。这捕获任何类,我的'子文件夹'外特别的。

换句话说的结果
我的解决方案来到了下来分离控制器,多一点这样 / API / XXXX 并没有得到太拥挤。


  • 我在UI项目创建一个文件夹(可以说组别),并把该文件夹中的API控制器。

  • Visual Studio中自然将根据文件夹类的命名空间。因此, WIDGET1 组别文件夹中获取的默认名称空间 UI.Category1.Widget1

  • 当然,我想API网址,以反映文件夹结构( / API /组别/小部件)。你在上面看到的第一个映射完成,通过硬编码 / API /组别进入路线,那么命名令牌限制将搜索匹配的控制器类。


  

注意:截至发稿 DataTokens 都默认为null。我不是
  知道这是一个错误,或功能。所以我写了一个小帮手
  方法并添加到我的 RouteConfig.cs 文件....


  r.AddRouteToken(命名空间,新的String [] {UI.Controllers.Category1});私人静态路由AddRouteToken(此路线r,字符串键,字符串[]值){
  //改变从RC到RTM ... datatokens为null
如果(r.DataTokens == NULL){
       r.DataTokens =新RouteValueDictionary();
    }
    r.DataTokens [关键] =价值;
    返回ř;
}


  

注2 :甚至认为这是一个的WebAPI 1篇文章,如@Jamie_Ide在评论中指出,上述方案不会的WebAPI 2工作,因为 IHttpRoute.DataTokens 有没有setter。为了解决这个问题,你可以使用这样一个简单的扩展方法:


 私有静态IHttpRoute MapHttpRoute(这HttpRouteCollection路线,字符串名称,字符串routeTemplate,对象默认值,对象的限制,字符串[] namespaceTokens)
{
    HttpRouteValueDictionary defaultsDictionary =新HttpRouteValueDictionary(默认);
    HttpRouteValueDictionary constraintsDictionary =新HttpRouteValueDictionary(约束);
    IDictionary的&LT;字符串对象&gt;令牌=新词典&LT;字符串对象&gt;();
                                tokens.Add(命名空间,namespaceTokens);    IHttpRoute路线= routes.CreateRoute(routeTemplate,defaultsDictionary,constraintsDictionary,dataTokens:令牌处理程序:NULL);
    routes.Add(名称,路线);    返回路线;
}

In the 5-minute video at the following link, at the 1:10 mark, Jon Galloway says that adding a method called DeleteComment to his CommentsController controller class will by convention map automatically to the delete http verb.

How does MVC with WebApi know how to rout the methods to the right verbs? I know the routing in the global.asax.cs file routes requests to the correct controller, but how does a delete request get "mapped by convention" to the delete method, or any method? Especially when there can be more than 1 method for each verb? "By convention" makes me think it's just looking at the first word in a method name ... but if so, it would have to read the signature of the methods to tell two delete methods or two get methods apart ... and where is all this defined?

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

Thanks!

Edit: Here is the code in the sample ValuesController class that comes in the WebApi template. This was the source of my original question. How does the "convention" that differentiates between these (and any other methods in the controller) work?

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

    // GET /api/values/5
    public string Get(int id)
    {
        return value;
    }

解决方案

I apologize in advance, this post strays a bit from what you asked, but all of this bubbled up when I read your question.

WebAPI Matching Semantic
The matching semantic used by (the default routes in) WebAPI is fairly simple.

  1. It matches the name of the action with the verb (verb = GET? look for method name starting with "get")
  2. if a parameter is passed, the api seeks an action with a parameter

So in your code sample a GET request without a parameter matches the Get*( ) function without an parameters. A Get containing and ID looks for a Get***(int id).

Examples
While the matching semantic is simple, it creates some confusion for MVC developers (well at least this developer). Lets look at some examples :

Odd Names - Your get method can be named anything, so long as it starts with "get". So in the case of a widget controller you can name your functions GetStrawberry() and it will still be matched. Think of the matching as something like : methodname.StartsWith("Get")

Multiple Matching Methods - What happens if you have two Get methods with no parameters? GetStrawberry() and GetOrange(). As best I can tell, the function defined first (top of the file) in your code wins ...strange. This has the side effect of making some methods in your controller unreachable (at least with the default routes)....stranger.

NOTE : the beta behaved as above for 'matching multiple methods' - the RC & Release version is a bit more OCD. It throws an error if there are multiple potential matches. This change removes the confusion of multiple ambiguous matches. At the same time, it reduces our ability to mix REST and RPC style interfaces in the same controller, relying on the order & overlapping routes.

What to do?
Well, WebAPI is new and consensus is still coalescing. The community seems to be reaching for REST principles quite a bit. Yet, not every API can or should be RESTful, some are more naturally expressed in an RPC style. REST & what people call REST seems to be the source of quite a bit of confusion, well at least to Roy Fielding.

As a pragmatist, i suspect that many API's will be 70% RESTful, with a smattering of RPC style methods. First, the the controller proliferation alone (given the webapi binding method) is going to drive developers bonkers. Second, WebAPI doesn't really have a built in way to create a nested structure of api paths (meaning: /api/controller/ is easy, but /api/CATEGORY/Sub-Category/Controller is doable, but a pain).

From my perspective, I would love to see the webAPI folder structure control the default API paths... meaning if I create a Category folder in my UI project then /api/Category would be the default path (something parallel to this MVC article).

What did I do?
So, I had a few requirements: (1) to be able to use restful syntax in most case, (2) have some "namespace" separation of controllers (think sub-folders), (3) be able to call additional rpc-like methods when necessary. Implementing these requirements came down to clever routing.

// 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 } );

  • The first two routes create "sub-folder" routes. I need to create a route for each sub-folder, but I limited myself to major categories, so I only end up with 3-10 of these. Notice how these routes add the Namespace data token, to restrict what classes are searched for a particular route. This corresponds nicely to the typical namespace setup as you add folders to a UI project.
  • The third route allows specific method names to be called (like traditional mvc). Since web API does away with action name in the URL, it's relatively easy to tell which calls want this route.
  • The last route entry is the default web api route. This catches any classes, particularly ones outside my 'sub-folders'.

Said Another Way
My solution came down to down to separating controllers a bit more so /api/XXXX didn't get too crowded.

  • I create a folder in my UI project(lets say Category1), and put api controllers within the folder.
  • Visual studio naturally sets class namespaces based on folder. So Widget1 in the Category1 folder gets a default namespace of UI.Category1.Widget1.
  • Naturally, I wanted api URLs to reflect the folder structure (/api/Category1/Widget). The first mapping you see above accomplishes that, by hard coding /api/Category1 into the route, then the namespace token restricts classes that will be searched for a matching controller.

NOTE: as of the release DataTokens are null by default. I'm not sure if this is a bug, or a feature. So I wrote a little helper method and added to my RouteConfig.cs file....

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: even thought this is a WebAPI 1 post, as @Jamie_Ide points out in the comments the above solution doesn't work in WebAPI 2 because IHttpRoute.DataTokens has no setter. To get around this you can use a simple extension method like this:

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;
}

这篇关于如何在MVC的WebAPI的方法映射到HTTP动词?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆