在异步操作中,FormsAuthentication.SetAuthCookie抛出NullReferenceException异常

14

我发现在 Azure 网站的异步操作中,FormsAuthentication.SetAuthCookie 会抛出 NullReferenceException - Object reference not set to an instance of an object。

我找到了以下信息:

http://connect.microsoft.com/VisualStudio/feedback/details/743350/formsauthentication-setauthcookie-throws-nullreferenceexception-if-called-in-an-async-action-in-mvc4

然而,我已经实现了:

 <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

我的代码在本地设置正常,但当我部署到 Azure 时就出现了问题。我正在使用 Azure 网站 (https://www.windowsazure.com/en-us/home/scenarios/web-sites/),我理解这通常使用 web.config 文件?我还尝试通过 Azure 控制面板添加 appsetting。

enter image description here

我已经尝试在我的异步方法中添加.ConfigureAwait(false);,但没有成功。

下面的代码将抛出异常:

public class TestController : Controller
    {
        public async Task<ActionResult> Index()
        {
            var httpResponse = await new HttpClient().GetAsync("http://www.google.com");
            FormsAuthentication.SetAuthCookie("test", true);
            return View();
        }
    }

有人知道我如何让这个工作吗?

更新:

堆栈跟踪:

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Threading.Tasks.<>c__DisplayClass1c.<GetRethrowWithNoStackLossDelegate>b__1b(Task task) +91
   System.Threading.Tasks.TaskHelpersExtensions.ThrowIfFaulted(Task task) +15
   System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult) +77
   System.Web.Mvc.Async.<>c__DisplayClass3f.<BeginInvokeAsynchronousActionMethod>b__3e(IAsyncResult asyncResult) +16
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +59
   System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +240
   System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +31
   System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +23
   System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +128
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +26
   System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14
   System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +41
   System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +28
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__4(IAsyncResult asyncResult) +28
   System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +31
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +23
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +59
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +96

更新

我发现手动设置Cookie可以使其正常工作。

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "test", DateTime.Now, DateTime.Now.AddMinutes(30), true, null, FormsAuthentication.FormsCookiePath);
            string encTicket = FormsAuthentication.Encrypt(ticket);
            Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

我不打算回答这个问题,因为我想知道为什么FormsAuthentication.SetAuthCookie会抛出异常,并且为什么它在Azure网站上的行为不同。


异常的堆栈跟踪是什么? - svick
你确定当前的网站支持未发布的ASP.NET 4.5吗? - Stephen Cleary
@svick 已添加了堆栈跟踪。 - Tom
@StephenCleary,已更新问题标签,应仅使用ASP.NET 4和Async Targeting Pack。 - Tom
1
我非常确定 UseTaskFriendlySynchronizationContext 仅支持 ASP.NET 4.5,但我也不认为这是你问题的原因。 - Stephen Cleary
3个回答

22

这个问题不仅限于Azure - 当在调用FormsAuthentication.SetAuthCookie之前使用await语句时,方法FormsAuthentication.SetAuthCookie将抛出空引用异常。

最简单的解决方案是使用以下代码:

        Response.Cookies.Add(FormsAuthentication.GetAuthCookie("user-1", true));

另一种选择是自己指定创建票据:

        var ticket = new FormsAuthenticationTicket(
            2,
            "user-1",
            DateTime.Now,
            DateTime.Now.AddDays(2),
            true,
            String.Empty,
            "/");

        var encTicket = FormsAuthentication.Encrypt(ticket);

        Response.Cookies.Add(new HttpCookie(".AUTH", encTicket));

你能提供一些证据来证明这是为什么吗?我遇到过这个问题好几次,但是找不到支持的文档。 - shortstuffsushi

0

目前,Windows Azure Web Sites不支持.NET 4.5。您需要将项目降级为.NET 4.0,这也意味着您需要重写代码以不使用async。

您可以尝试bin deploy应用程序所需的.NET 4.5程序集


该应用程序正在使用带有Async Targeting Pack的.NET 4。我认为Stephen Cleary指出的“UseTaskFriendlySynchronizationContext”仅在.NET 4.5中受支持,这就是它在Azure上失败而在本地没有失败的原因。 - Tom

0

你也可以改变这种方式,取决于你自己

public ActionResult Index()
{
    var t1 = Task.Run(() => HttpClient().GetAsync("http://www.google.com"));
    t1.Wait();
    FormsAuthentication.SetAuthCookie("test", true);
    return View();
}

这种方法对我很有效,我从GlennMorton的第一个答案中得到了灵感。

谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接