1

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

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

glimpsePolicy=On

当它工作时(瞥见关闭)有两个饼干

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();
    }
4

1 回答 1

1

失败的原因是 Postal 库HttpContext在将电子邮件视图渲染为CreateControllerContextPostal 类中的反编译方法时创建了自己的实例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 相关的调用。

我们有一个类似的问题,我对为什么这不起作用给出了类似的回应。

更新 :

我在上面提到过之前报告过类似的问题,但是当我试图找到一个更合适的解决方案时,似乎这种情况在这方面略有不同,另一个类似的问题实际上执行了一个带有新创建的上下文的控制器,结果在NullReferenceExceptionGlimpse 特定代码中,而在这里我们得到一个NullReferenceException内部 MVC 特定代码,尽管它是由 Glimpse 触发的。

System.ArgumentNullException:值不能为空。

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

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

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 时禁用 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();
    }
}
于 2014-01-12T13:30:30.013 回答