31

次のリンクの5分間のビデオで、1:10の時点で、Jon Gallowayは、DeleteCommentというメソッドをCommentsControllerコントローラークラスに追加すると、慣例により、deletehttp動詞に自動的にマップされると述べています。

WebApiを使用するMVCは、メソッドを適切な動詞にルーティングする方法をどのように知っていますか?global.asax.csファイルのルーティングはリクエストを正しいコントローラーにルーティングすることを知っていますが、削除リクエストはどのようにしてdeleteメソッドまたは任意のメソッドに「慣例によりマッピング」されますか?特に、動詞ごとに複数のメソッドが存在する可能性がある場合はどうでしょうか。「慣例により」は、メソッド名の最初の単語を見ているだけだと思います...しかし、そうであれば、2つのdeleteメソッドまたは2つのgetメソッドを区別するためにメソッドのシグネチャを読み取る必要があります...そしてどこにこれはすべて定義されていますか?

ビデオ: 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;
    }
4

2 に答える 2

60

事前にお詫び申し上げます。この投稿はあなたの質問から少し外れていますが、あなたの質問を読んだときに、このすべてが泡立ちました。

WebAPIマッチングセマンティックWebAPI
(のデフォルトルート)で使用されるマッチングセマンティクスは非常に単純です。

  1. アクションの名前を動詞と一致させます(動詞= GET?「get」で始まるメソッド名を探します)
  2. パラメータが渡された場合、APIはパラメータを使用してアクションを探します

したがって、コードサンプルでは、​​パラメーターのないGETリクエストは、パラメーターのGet*( )ない関数と一致します。含むGetとIDはを探しますGet***(int id)


マッチングセマンティクスは単純ですが、MVC開発者(少なくともこの開発者)にとっては混乱を招きます。いくつかの例を見てみましょう:

奇数名-getメソッドは、「get」で始まる限り、任意の名前を付けることができます。したがって、ウィジェットコントローラーの場合は、関数GetStrawberry()に名前を付けることができますが、それでも一致します。マッチングは次のようなものと考えてください。 methodname.StartsWith("Get")

複数のマッチングメソッド-パラメータのないGetメソッドが2つある場合はどうなりますか? GetStrawberry()およびGetOrange()。私が知る限り、コードで最初に定義された関数(ファイルの先頭)が...strangeに勝ちます。これには、コントローラーの一部のメソッドに到達できなくなるという副作用があります(少なくともデフォルトルートでは)...見知らぬ人。

:ベータ版は「複数の方法を一致させる」ために上記のように動作しました-RC&リリースバージョンはもう少しOCDです。一致する可能性のあるものが複数ある場合は、エラーがスローされます。この変更により、複数のあいまいな一致の混乱がなくなります。同時に、順序と重複するルートに依存して、同じコントローラー内でRESTスタイルとRPCスタイルのインターフェイスを混在させる機能が低下します。

何をすべきか?
ええと、WebAPIは新しく、コンセンサスはまだ合体しています。コミュニティはRESTの原則にかなり手を伸ばしているようです。ただし、すべてのAPIがRESTfulである、またはそうである必要があるわけではありません。一部のAPIは、RPCスタイルでより自然に表現されます。RESTと人々がRESTと呼ぶものは、少なくともRoy Fieldingにとっては、かなり の混乱の原因のようです。

実用主義者として、私は多くのAPIが70%RESTfulであり、RPCスタイルのメソッドがわずかにあると思います。まず、コントローラーの急増だけで(webapiバインディングメソッドが与えられた場合)、開発者は大騒ぎになります。第2に、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 } );
  • 最初の2つのルートは、「サブフォルダー」ルートを作成します。サブフォルダごとにルートを作成する必要がありますが、主要なカテゴリに限定したため、最終的には3〜10個になります。これらのルートがデータトークンを追加しNamespaceて、特定のルートで検索されるクラスを制限する方法に注目してください。これは、UIプロジェクトにフォルダーを追加するときの一般的な名前空間の設定にうまく対応しています。
  • 3番目のルートでは、特定のメソッド名を呼び出すことができます(従来のMVCのように)。Web APIはURL内のアクション名を廃止するため、どの呼び出しがこのルートを必要としているかを比較的簡単に判断できます。
  • 最後のルートエントリは、デフォルトのWebAPIルートです。これは、すべてのクラス、特に私の「サブフォルダー」の外にあるクラスをキャッチします。

別の言い方
をすれば、私の解決策は、コントローラーをもう少し分離することでしたので、/api/XXXX混雑しすぎませんでした。

  • UIプロジェクト(たとえばCategory1)にフォルダーを作成し、そのフォルダー内にAPIコントローラーを配置します。
  • Visual Studioは、フォルダーに基づいてクラスの名前空間を自然に設定します。したがってWidget1Category1フォルダ内のデフォルトの名前空間は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;
}

注2:@Jamie_Ideがコメントで指摘しているように、これはWebAPI 1の投稿であるとIHttpRoute.DataTokensはいえ、セッターがないため、上記のソリューションはWebAPI2では機能しません。これを回避するには、次のような単純な拡張方法を使用できます。

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;
}
于 2012-05-06T15:44:55.413 に答える
3

これはかなり頻繁に発生します。そして、それについてはさまざまな見方があります。私は個人的に今のところ特定のアイデアを購読していませんが、リソースごとに1つのコントローラーを備えたアイデアがRESTコミュニティで最も人気があるようです。

つまり、基本的に次のことができます。

  1. ルート内のアクションを公開します(Web APIはactionMVCと同様に単語を扱います)が、これは通常、使用することを意図したものではありません。
  2. この投稿に従って、さまざまなパラメータでメソッドを定義します
  3. 私が言ったように、最も推奨されるのは、リソースごとに1つのコントローラーを使用することです。そのため、Web APIサンプルでも、エンティティの収集用のコントローラーは、エンティティ自体のコントローラーとは異なります。ロブ・コナリーによるこの投稿を読んでください、そしてここにグレンの答えがあります。
于 2012-05-03T22:47:35.737 に答える