web-dev-qa-db-ja.com

別のプロジェクトでコントローラーを探すようにWeb Api 2を構成する方法は? (Web Apiで使用していたように)

コントローラーをMvc Web Apiの別のクラスライブラリプロジェクトに配置していました。以前は、Web APIプロジェクトのglobal.asaxに次の行を追加して、別のプロジェクトでコントローラーを探していました。

ControllerBuilder.Current.DefaultNamespaces.Add("MyClassLibraryProject.Controllers");

上記の行を追加する以外は、他の構成を行う必要はありませんでした。これはいつも私にとってはうまくいきました。

ただし、上記の方法を使用してWebApi2で同じことを行うことはできません。うまくいきません。 WebApi2プロジェクトは、自身のプロジェクトのコントローラーフォルダーでコントローラーを見つけようとします。

-2か月後に要約の更新をほとんど行わない(これに報いを始めたため):

WebApiOneソリューションを作成しました。2つのプロジェクトがあり、最初のプロジェクトはWebApiプロジェクトで、2番目のプロジェクトはコントローラーのクラスライブラリです。コントローラクラスライブラリプロジェクトへの参照をWebApiプロジェクトに追加すると、すべてが期待どおりに機能します。つまり、 http://mydevdomain.com/api/values に移動すると、正しい出力が表示されます。

WebApiTwoという2番目のプロジェクトを作成しました。2つのプロジェクトがあり、最初のプロジェクトはWebApi2プロジェクト、2番目はコントローラーのクラスライブラリです。コントローラクラスライブラリプロジェクトへの参照をWebApi2プロジェクトに追加すると、期待どおりに機能しません。つまり、 http://mydevdomain.com/api/values にアクセスすると、「「values」という名前のコントローラーに一致するタイプが見つかりませんでした。」

最初のプロジェクトでは、カスタム設定をまったく行っていません。

ControllerBuilder.Current.DefaultNamespaces.Add("MyClassLibraryProject.Controllers");

私のglobal.asaxでは、StrathWebが彼の2つのブログ投稿で提案したカスタムソリューションを実装していません。これは、コントローラープロジェクトの参照をWebApiプロジェクトに追加するだけですべてが機能するためです。

だから、私はすべてがWebApi2でも同じように動作することを期待するでしょう...しかし、そうではありません。 WebAPi2でこれを実際に試した人はいますか?

49
M. Ali Iftikhar

これが正常に機能することを確認しました。確認事項:

参照:メインのWeb APIプロジェクトは外部クラスライブラリを参照していますか?

ルーティング:外部コントローラーに干渉する可能性のあるルートを設定しましたか?

保護レベル:外部ライブラリのコントローラーはpublicですか?

継承:外部ライブラリのコントローラーはApiControllerを継承しますか?

バージョン管理: Web APIプロジェクトとクラスライブラリの両方が同じバージョンのWeb APIライブラリを使用していますか?

問題が解決した場合は、テストソリューションをパッケージ化し、利用できるようにします。また、注意すべき点として、Global.asaxに追加した行を持つコントローラーを検索するようにWeb APIに指示する必要はありません。システムはそれらが参照されていれば自動的にコントローラーを検索します。

29
DavidG

そのまま動作するはずです。チェックリスト

  • ApiControllerを継承
  • Controllerでコントローラー名を終了します。例えば。 ValuesController
  • WebApiプロジェクトとクラスライブラリプロジェクトが同じWebApiアセンブリを参照していることを確認してください
  • 属性ルーティングを使用してルートを強制しようとする
  • Cleanソリューション、手動でbinフォルダーを削除して再構築
  • Temporary ASP.NET Filesフォルダーを削除します。 WebApiおよびMVCキャッシュコントローラーのルックアップ結果
  • `config.MapHttpAttributeRoutes();を呼び出します。フレームワークが属性ルートを考慮するようにする
  • 呼び出しているメソッドが正しいHTTP動詞を処理するように作られていることを確認してください(GET Webメソッドの場合、ブラウザURLを介して呼び出すことができます。POSTウェブリクエスト)

このコントローラー:

[RoutePrefix("MyValues")]
public class AbcController : ApiController
{
    [HttpGet]
    [Route("Get")]
    public string Get()
    {
        return "Ok!";
    }
}

このURLに一致します:

http://localhost/MyValues/GetRoutePrefixで指定されていないため、ルートに/api/がないことに注意してください。


コントローラールックアップキャッシュ: これは既定のコントローラーリゾルバーです 。ソースコードで、検索結果がキャッシュされていることがわかります。

/// <summary>
/// Returns a list of controllers available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Type}" /> of controllers.</returns>
public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)
{
    HttpControllerTypeCacheSerializer serializer = new HttpControllerTypeCacheSerializer();

    // First, try reading from the cache on disk
    List<Type> matchingTypes = ReadTypesFromCache(TypeCacheName, IsControllerTypePredicate, serializer);
    if (matchingTypes != null)
    {
        return matchingTypes;
    }
...
}
16

同じシナリオに直面していて、@ justmaraが私を正しい道に導きました。 @justmaraの答えから依存アセンブリの強制ロードを達成するためのhowは次のとおりです。

1)DefaultAssembliesResolverクラスをオーバーライドします

_public class MyNewAssembliesResolver : DefaultAssembliesResolver
{
    public override ICollection<Assembly> GetAssemblies()
    {

        ICollection<Assembly> baseAssemblies = base.GetAssemblies();
        List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
        var controllersAssembly = Assembly.LoadFrom(@"Path_to_Controller_DLL");
        baseAssemblies.Add(controllersAssembly);
        return baseAssemblies;

    }
}
_

2)設定セクションで、デフォルトを新しい実装に置き換えます

_config.Services.Replace(typeof(IAssembliesResolver), new MyNewAssembliesResolver());
_

このブログのポインターを使用して、この構文をまとめました。

http://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/

他の人が言ったように、コントローラを直接参照することで強制的にロードする場合、この問題に直面しているかどうかを知っています。別の方法は、CurrentDomain.GetAssemblies()の結果を例にして、アセンブリがリストにあるかどうかを確認することです。

また、OWINコンポーネントを使用してセルフホスティングしている場合は、これに遭遇します。テスト時には、最初のWebAPI要求が送信されるまでDefaultAssembliesResolverが起動しないことに注意してください(それを実現するのに時間がかかりました)。

9
user1821052

IAssembliesResolverサービスが呼び出される前に、参照されるアセンブリがロードされたことを確認しますか?次のようなダミーコードをアプリケーションに挿入してみてください。

var a = new MyClassLibraryProject.Controllers.MyClass();

設定方法(ただし、「a」を使用しない場合、コンパイラはこのコードを「最適化」して完全に削除できることを忘れないでください)。アセンブリの読み込み順序でも同様の問題が発生しました。起動時に依存アセンブリを強制的に読み込むことになりました。

7
justmara

参照先のアセンブリを読み込むようwebapi/mvcに指示する必要があります。それには、web.configのコンパイル/アセンブリセクションを使用します。

<compilation debug="true" targetFramework="4.5.2">
  <assemblies>
    <add Assembly="XYZ.SomeAssembly" />
  </assemblies>
</compilation>

そのような単純な。 @ user1821052が提案した方法でコードでそれを行うことができますが、このweb.configバージョンには同じ効果があります。

5
Ryan Mann

すでに言われたこと以外に:

異なる名前空間に同じ名前の2つのコントローラーがないことを確認してください。

1つのコントローラー(foo.UserApiController)を新しいネームスペース(bar.UserApiController)とURIに部分的に移行する必要がある場合がありました。古いコントローラーは慣例により/ userapiにマップされ、新しいコントローラーはRoutePrefix["api/users"]を介して属性ルーティングされました。新しいコントローラーは、名前をbar.UserFooApiControllerに変更するまで機能しませんでした。

2
Igor Lankin

AttributeRoutingを使用する場合、特にコントローラークラスでRoute属性を使用している場合、RoutePrefix属性を使用してメソッドを修飾することは簡単に忘れられます。コントローラーアセンブリは、Web APIパイプラインによってピックアップされなかったようです。

1
ngu

クラスライブラリがEFで構築されている場合、クラスライブラリプロジェクトのApp.configで指定された接続文字列があることを確認してください[〜 #〜] and [〜#〜] Web API MVCプロジェクトのWeb.configで。

0
CodeCabbie