2014-03-05 136 views
4

我有一個相當奇怪的場景。我在具有MVC4頁面和Web API端點的項目上使用autofac。 Autofac正在管理我的模型對象(它反過來管理數據庫上下文的作用域)的作用域,我將它配置爲InstancePerHttpRequestInstancePerApiRequest,以便每個請求只創建一個數據庫上下文。這使得我使用的任何數據庫對象都被附加到相同的請求(上下文是EF6上下文)。區分應用程序處理程序中的Web API請求和MVC請求

這是我奇怪的場景:應用程序支持「代理」功能,其中一個用戶可以在頁面請求期間成爲另一個用戶。爲了確保即使AuthorizeAttribute都注意到這一點,我正在捕獲PostAuthenticateRequest事件中的請求,並執行IPrincipal切換,並設置我的數據庫上下文使用的用戶。問題出現在權限檢查中:我需要問我的數據庫是否允許用戶代理他們想代理的用戶。該代碼是或多或少如下:

protected void Application_PostAuthenticateRequest() 
{ 
    if (!User.Identity.IsAuthenticated) 
      return; //nothing to do here if we are not authenticated 

    var cookie = Request.Cookies.Get(Controllers.ProxyController.ProxyCookie); 

    //The uh-oh occurs at this line...read the rest of the question 
    var model = DependencyResolver.Current.GetService<Model.MyModel>(); 

    //we load the user initially 
    //this sets up the user for the rest of the request since our model object should be shared for everything 
    var user = model.Users.Where(u => u.username == User.Identity.Name).FirstOrDefault(); 
    model.User.Current = model.User.Actual = user; 

    if (cookie != null) 
    { 
     //we ask the database if this user can proxy as the cookie'd username 
     var proxyAs = model.Users.Where(u => u.username == cookie.Value).FirstOrDefault(); 
     if (user != null && proxyAs != null) 
     { 
      if (user.CanProxyAs(proxyAs)) 
      { 
       //this user is allowed to proxy as the specified user 
       string[] roles; 
       if (proxyAs != null) 
       { 
        //get their roles to replace ours 
        roles = proxyAs.groups.SelectMany(g => g.roles).Select(r => r.name).ToArray(); 
       } 
       else 
       { 
        //no roles according to the database 
        roles = new string[0]; 
       } 

       //set the model user stuff 
       model.User.Actual = user; 
       model.User.Current = proxyAs; 

       //save the original user IPrincipal 
       HttpContext.Current.Items[Controllers.ProxyController.ProxyUser] = User; 

       //we need to set the thread current principal as well to keep it in sync: 
       // MVC3 stuff (controllers) appears to use HttpContext.Current.User 
       // MVC4 stuff (web api) appears to use Thread.CurrentPrincipal 
       Thread.CurrentPrincipal = HttpContext.Current.User = new GenericPrincipal(new GenericIdentity(cookie.Value), roles); 
      } 
     } 
    } 
} 

的問題是在這裏我用DependencyResolver來解決模型對象的一部分。

這對於MVC請求完全沒有問題......他們以後使用DependencyResolver來創建控制器。根據autofac MVC文檔設置的AutofacDependencyResolverAutofacDependencyResolver

對於Web API請求,但是這是一個問題。由於我使用了AutofacDependencyResolver來解析此方法中的模型對象,因此該模型的一個實例已被實例化爲生命週期爲InstancePerHttpRequest。但是,WebAPI將使用GlobalConfiguration.Configuration.DependencyResolver,它根據autofac Web API文檔設置爲AutofacWebApiDependencyResolver。最終發生的事情是,由於MVC DependencyResolver給了我們一個不同的模型,所以WebAPI調用使用的模型對象沒有設置它的model.User屬性。

我總是可以設置兩個對象,但是我們有上下文問題。我永遠不知道使用哪個解析器:我應該使用MVC DependencyResolver還是Web API依賴解析器?

更復雜的是,web api依賴關係解析器只能在HttpRequestMessage(即在Action方法內)的上下文中操作。該對象在PostAuthenticateRequest期間不可用。

我的問題

我可以以某種方式告訴autofac它需要使用相同的對象解決兩個InstancePerApiRequestInstancePerHttpRequest時?

如果這是不可能的,有沒有什麼辦法可以:

  • 弄清楚,如果請求是一個API請求或MVC請求和
  • 獲取期間PostAuthenticateRequestHttpRequestMessage如果它是一個API請求並用它來解決依賴關係?

我可能還需要認真反思我這樣做的方式,因爲這似乎乍一看不可能每this答案。

編輯:我相信我可以使用System.Net.Http.DelegatingHandler攔截WebAPI請求,這樣在PostAuthenticateRequest期間不需要獲取HttpRequestMessage。然而,這仍然存在確定請求是否是web api請求的問題。

+0

您可以手動爲給定的URL調用路由方法,並查看得到的路由是否指向Controller或ApiController並從那裏出去。 – Haney

+0

所以,據推測在最新版本中,密鑰應該在webapi和mvc之間共享,所以它應該以相同的方式工作。我以前在這個主題上寫過博客:http://darrenkopp.com/posts/2013/07/02/Getting-Autofac-NServiceBus-ASPNET-MVC-and-Web-API-to-play-together –

+0

我只是從NuGet中安裝了所有這些,並且從Web API操作的上下文調用斷言this.Model == DependencyResolver.Current.GetService ()'失敗,該操作通過注入'Model.MyModel'控制器的構造函數,並分配給'this.Model',它告訴我該實例不被共享。 –

回答

0

當您實施委託處理程序時,可以保證您的代碼在Web API請求中運行。你也可以把它做成一個全局過濾器。

您或許可以將MVC部分實現爲高優先級授權過濾器,並且您將再次保證在此時位於MVC中。