使用Glimpse和Postal时抛出异常

时间:2014-01-10 21:09:07

标签: asp.net-mvc-5 glimpse postal

我刚刚开始在我的MVC5项目中使用Glimpse,当我使用Postal发送电子邮件而不禁用Glimpse时遇到了问题。我已经能够将其缩小到两个软件包的问题 - 如果没有打开Glimpse cookie,就不会发生这种情况。

在Fiddler,我检查了两者之间的区别。当它抛出异常时,cookie就是

glimpsePolicy=On
当它工作时(Glimpse关闭)有两个饼干

glimpseId=FBar; glimpsePolicy=

我得到的例外是

System.ArgumentNullException: Value cannot be null.
Parameter name: controllerContext
   at System.Web.Mvc.ChildActionValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
   at Castle.Proxies.Invocations.ValueProviderFactory_GetValueProvider.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Glimpse.Core.Extensibility.ExecutionTimer.Time(Action action)
   at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.ValueProviderFactoryProxy.GetValueProvider(ControllerContext controllerContext)
   at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
   at System.Web.Mvc.ControllerBase.get_ValueProvider()
   at Glimpse.Mvc.Message.ActionMessageExtension.AsActionMessage[T](T message, ControllerBase controller)
   at Glimpse.Mvc.AlternateType.ViewEngine.FindViews.PostImplementation(IAlternateMethodContext context, TimerResult timerResult)
   at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.IViewEngineProxy.FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
   at System.Web.Mvc.ViewEngineCollection.<>c__DisplayClass6.<FindView>b__4(IViewEngine e)
   at System.Web.Mvc.ViewEngineCollection.Find(Func`2 lookup, Boolean trackSearchedPaths)
   at System.Web.Mvc.ViewEngineCollection.Find(Func`2 cacheLocator, Func`2 locator)
   at Postal.EmailViewRenderer.Render(Email email, String viewName)
   at Postal.EmailService.Send(Email email)
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid1[T0](CallSite site, T0 arg0)
   at System.Web.Mvc.ActionMethodDispatcher.<>c__DisplayClass1.<WrapVoidAction>b__0(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__36(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at Castle.Proxies.Invocations.AsyncControllerActionInvoker_EndInvokeActionMethod.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Glimpse.Mvc.AlternateType.AsyncActionInvoker.EndInvokeActionMethod.NewImplementation(IAlternateMethodContext context)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.AsyncControllerActionInvokerProxy.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3c()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass45.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3e()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass30.<BeginInvokeActionMethodWithFilters>b__2f(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<>c__DisplayClass28.<BeginInvokeAction>b__19()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<BeginInvokeAction>b__1b(IAsyncResult asyncResult)

我创建了一个快速操作来测试它。控制器代码是:

public void TestEmailExt()
    {
    var confirmationToken = "ConfirmationToken";
    var Phone1 = "**********";
    dynamic email = new Email("RegEmail");
    email.To = "**@gmail.com";
    email.UserName = "UserName";
    email.ConfirmationToken = confirmationToken;
    email.Phone = Extensions.Right(Phone1, 4);
    if (email.To.Contains("@mydomain"))
        email.From = INTERNAL_EMAIL_FROM;
    else
        email.From = EXTERNAL_EMAIL_FROM;
    email.Send();
    }

1 个答案:

答案 0 :(得分:1)

此失败的原因是因为Postal库在呈现电子邮件视图时创建了自己的HttpContext实例,因为Postal CreateControllerContext类中的反编译EmailViewRenderer方法显示:

private ControllerContext CreateControllerContext()
{
  HttpContextWrapper httpContextWrapper = new HttpContextWrapper(new HttpContext(new HttpRequest("", this.UrlRoot(), ""), new HttpResponse(TextWriter.Null)));
  RouteData routeData = new RouteData();
  routeData.Values["controller"] = (object) this.EmailViewDirectoryName;
  return new ControllerContext(new RequestContext((HttpContextBase) httpContextWrapper, routeData), (ControllerBase) new EmailViewRenderer.StubController());
}

这意味着Glimpse在BeginRequest处执行的设置已完全删除,而挂钩仍然可以拦截与MVC相关的调用。

我们有一个similar issue,我给了similar response这就是为什么这不起作用。

更新:

我在上面提到过,之前已经报道了一个类似的问题,但是当我试图找到一个更合适的解决方案时,似乎这个案例在这方面略有不同,其他类似的问题实际上执行了一个新的控制器在Glimpse特定代码中创建了导致NullReferenceException的上下文,而在此我们在MVC特定代码中获得了NullReferenceException,尽管是由Glimpse触发的。

  
    

System.ArgumentNullException:值不能为空。

         

参数名称:controllerContext
    在System.Web.Mvc.ChildActionValueProviderFactory.GetValueProvider(ControllerContext controllerContext)

  

我们得到的异常是因为ControllerContext实例(内联创建)上的StubController属性为null,通常在执行控制器时设置(这不是这里的情况)

因此,我在下面提出的解决方法仍然适用,但如果稍微修改上述CreateControllerContext()的代码,则可以避免:

private ControllerContext CreateControllerContext()
{
  HttpContextWrapper httpContextWrapper = new HttpContextWrapper(new HttpContext(new HttpRequest("", this.UrlRoot(), ""), new HttpResponse(TextWriter.Null)));
  RouteData routeData = new RouteData();
  routeData.Values["controller"] = (object) this.EmailViewDirectoryName;
  // MODIFIED
  var stubController = new EmailViewRenderer.StubController();
  var controllerContext = new ControllerContext(new RequestContext(httpContextWrapper, routeData), stubController);
  stubController.ControllerContext = controllerContext;
  return controllerContext;
}

我在Postal issue tracker

上为此创建了一个问题

END OF UPDATE

我认为目前最好的解决方案是在调用Postal时禁用Glimpse,然后再恢复正常的Glimpse行为。我们可能会在即将发布的版本之一中将这种或那种方式包含在Glimpse Core库中,因为在请求处理逻辑的特定部分期间禁用Glimpse似乎并不常见,但是现在以下代码段可能会帮助你(注意它使用了Glimpse内部密钥,但不会保证在即将发布的版本中出现)

public class GlimpseSuppressionScope : IDisposable
{
    private const string GlimpseRequestRuntimePermissionsKey = "__GlimpseRequestRuntimePermissions";
    private readonly HttpContext currentHttpContext;
    private readonly RuntimePolicy? currentRuntimePolicy;
    private bool disposed;

    public GlimpseSuppressionScope(HttpContext currentHttpContext)
    {
        if (currentHttpContext == null)
        {
            throw new ArgumentNullException("currentHttpContext");
        }

        this.currentHttpContext = currentHttpContext;
        this.currentRuntimePolicy = this.currentHttpContext.Items[GlimpseRequestRuntimePermissionsKey] as RuntimePolicy?;
        this.currentHttpContext.Items[GlimpseRequestRuntimePermissionsKey] = RuntimePolicy.Off;
    }

    ~GlimpseSuppressionScope()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                if (this.currentHttpContext != null)
                {
                    this.currentHttpContext.Items.Remove(GlimpseRequestRuntimePermissionsKey);
                    if (this.currentRuntimePolicy.HasValue)
                    {
                        this.currentHttpContext.Items[GlimpseRequestRuntimePermissionsKey] = this.currentRuntimePolicy.Value;
                    }
                }
            }

            this.disposed = true;
        }
    }
}

然后可以在控制器操作方法中使用,如下所示:

public void TestEmailExt()
{
    using (new GlimpseSuppressionScope(System.Web.HttpContext.Current))
    {
        var confirmationToken = "ConfirmationToken";
        var Phone1 = "**********";
        dynamic email = new Email("RegEmail");
        email.To = "**@gmail.com";
        email.UserName = "UserName";
        email.ConfirmationToken = confirmationToken;
        email.Phone = Extensions.Right(Phone1, 4);
        if (email.To.Contains("@mydomain"))
            email.From = INTERNAL_EMAIL_FROM;
        else
            email.From = EXTERNAL_EMAIL_FROM;
        email.Send();
    }
}