![](/img/trans.png)
[英]ASP.Net MVC 5 - Correct way to access HttpContext.Current.User outside of controller?
[英]Correct way to use HttpContext.Current.User with async await
我正在處理異步操作並像這樣使用 HttpContext.Current.User
public class UserService : IUserService
{
public ILocPrincipal Current
{
get { return HttpContext.Current.User as ILocPrincipal; }
}
}
public class ChannelService : IDisposable
{
// In the service layer
public ChannelService()
: this(new Entities.LocDbContext(), new UserService())
{
}
public ChannelService(Entities.LocDbContext locDbContext, IUserService userService)
{
this.LocDbContext = locDbContext;
this.UserService = userService;
}
public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id)
{
var currentMemberId = this.UserService.Current.Id;
// do some async EF request …
}
}
// In the controller
[Authorize]
[RoutePrefix("channel")]
public class ChannelController : BaseController
{
public ChannelController()
: this(new ChannelService()
{
}
public ChannelController(ChannelService channelService)
{
this.ChannelService = channelService;
}
// …
[HttpGet, Route("~/api/channels/{id}/messages")]
public async Task<ActionResult> GetMessages(long id)
{
var channel = await this.ChannelService
.FindOrDefaultAsync(id);
return PartialView("_Messages", channel);
}
// …
}
我最近重構了代碼,以前我必須在每次調用服務時給用戶。 現在我讀了這篇文章https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/我不確定我的代碼仍然有效。 有沒有人有更好的方法來處理這個問題? 我不想向用戶提供對服務的每個請求。
只要您的web.config
設置正確, async
/ await
可以與HttpContext.Current
完美配合。 我建議將httpRuntime
targetFramework
設置為4.5
以刪除所有“怪癖模式”行為。
一旦完成,普通的async
/ await
將工作得很好。 如果您在另一個線程上工作或者您的await
代碼不正確,您只會遇到問題。
一、“其他線程”問題; 這是您鏈接到的博客文章中的第二個問題。 像這樣的代碼當然不能正常工作:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
這個問題實際上與異步代碼無關; 它與從(非請求)線程池線程中檢索上下文變量有關。 如果您嘗試同步執行,則會出現完全相同的問題。
核心問題是異步版本使用了假異步。 這不合適,尤其是在 ASP.NET 上。 解決方案是簡單地刪除假異步代碼並使其同步(或真正異步,如果它確實有真正的異步工作要做):
void Method()
{
var user = _userService.Current;
...
}
鏈接博客中推薦的技術(包裝HttpContext
並將其提供給工作線程)非常危險。 HttpContext
被設計為一次只能從一個線程訪問,而 AFAIK 根本不是線程安全的。 所以在不同的線程之間共享它是在尋求一個傷害的世界。
如果await
代碼不正確,則會導致類似的問題。 ConfigureAwait(false)
是庫代碼中常用的一種技術,用於通知運行時它不需要返回到特定上下文。 考慮這個代碼:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
在這種情況下,問題很明顯。 ConfigureAwait(false)
告訴 ASP.NET 當前方法的其余部分不需要上下文,然后它立即訪問該上下文。 但是,當您開始在接口實現中使用上下文值時,問題就不那么明顯了:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
這段代碼同樣是錯誤的,但並沒有那么明顯錯誤,因為上下文隱藏在接口后面。
因此,一般准則是:如果您知道該方法不依賴於其上下文(直接或間接),則使用ConfigureAwait(false)
); 否則,不要使用ConfigureAwait
。 如果它在你的設計中可以接受的接口實現在執行使用上下文,然后調用接口方法不應使用任何方法ConfigureAwait(false)
:
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
只要您遵循該准則, async
/ await
將與HttpContext.Current
完美配合。
異步沒問題。 問題是當您將作品發布到不同的線程時。 如果您的應用程序設置為 4.5+,異步回調將發布在原始上下文中,因此您還將擁有正確的HttpContext
等。
無論如何,您不想訪問不同線程中的共享狀態,並且使用Task
,您很少需要顯式處理 - 只需確保將所有輸入作為參數,並且只返回響應,而不是讀取或寫入到共享狀態(例如HttpContext
、靜態字段等)
沒有問題,如果您的ViewModels.DisplayChannel
是一個沒有附加邏輯的簡單對象。
如果您的Task
的結果引用了“某些上下文對象”,則可能會出現問題,例如HttpContext.Current
。 此類對象通常附加到線程,但await
之后的整個代碼可能會在另一個線程中執行。
請記住, UseTaskFriendlySynchronizationContext
並不能解決您的所有問題。 如果我們談論的是 ASP.NET MVC,此設置可確保Controller.HttpContext
包含正確的值,就像之前一樣await
和之后。 但它並不能確保HttpContext.Current
包含正確的值,並且在await
之后它仍然可以為null 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.