调用 Controller.Execute 时的 NullReferenceException

本文关键字:NullReferenceException 时的 Execute Controller 调用 | 更新日期: 2023-09-27 18:31:37

我遇到了一些通过派生控制器类调用Controller.Execute的代码。派生类 ErrorController 不会重写Execute,传入的 RequestContext 参数不为 null,尽管它的几个属性是 null。如何确定RequestContext的哪个部分是导致"执行"抛出NullReferenceException的问题?

以下是调用Execute的代码:

public class AuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        try
        {
            throw new Exception();
            if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated)
            {
                signInClient.TransformClaimsBasedOnUserRole(incomingPrincipal.Identity.AsClaimsBasedIdentitiy());
            }
            return base.Authenticate(resourceName, incomingPrincipal);
        }
        catch (Exception e)
        {
        var context = HttpContext.Current; 
            var routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "Index");
            routeData.Values.Add("errorId", logId);
            routeData.Values.Add("exceptionMessage", "");
            IController controller = new ErrorController();
            var ctx = new RequestContext(new HttpContextWrapper(context), routeData);
            controller.Execute(ctx);
        }
    }
}

我不得不溜进投掷中以重现Execute例外。其他身份验证代码仅在极少数情况下抛出。

根据要求:

public class ErrorController: Controller
{
    public ActionResult Index(Guid? errorId, string exceptionMessage)
    {
        ErrorModel resultModel;
        try
        {
            resultModel = new ErrorModel
            {
                ErrorId = errorId==null ? Guid.NewGuid() : Guid.Parse(errorId.ToString()) ,
                ErrorMessage = (string.IsNullOrEmpty(exceptionMessage)) ? ConfigurationManager.AppSettings["GenericError"] : exceptionMessage,
            };
            if (User.IsInRole(RoleIdentifiers.InActive))
            {
               Authentication.AuthenticationManager.SignOut();
            }
        }
        catch (Exception e)
        {
            LogProvider.Current.LogError(LogLevel.Fatal, e, "Error constructing error result model for error Id [{0}]", errorId);
            return new HttpNotFoundResult();
        }
        return View(resultModel);
    }
    public ActionResult SessionTimeOut(string rtnController = "Home", string rtnAction="Index")
    {
        return View(new SessionTimeOutViewModel { RedirectAction = rtnAction, RedirectController = rtnController });
    }
    public ActionResult LogonAgain()
    {
        return null;
    }
}

而且,期待已久的堆栈跟踪:

   at System.Web.Mvc.AuthorizeAttribute.AuthorizeCore(HttpContextBase httpContext)
   at System.Web.Mvc.AuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at MyCompany.Authentication.AuthenticationManager.Authenticate(String resourceName, ClaimsPrincipal incomingPrincipal) in c:'Development'Give4GoodGallery'ThreeFifteen.Plexus.Web'Authentication'AuthenticationManager.cs:line 63

仔细观察,我发现这看起来像是关于AuthorizeAttribute的 - 这可能会重述直接调用Execute时不存在的某种上下文。

调用 Controller.Execute 时的 NullReferenceException

查看堆栈跟踪可以看出异常是在AuthorizeAttribute.AuthorizeCore中抛出的。这是一个简单的方法,它将访问HttpContextBaseUser 属性以及此用户属性的 Identity 属性。据我所知,这些属性之一必须为 null 才能抛出NullReferenceException

最有可能的是,HttpContext.CurrentUser 属性为空。这似乎并不奇怪,因为您正在调用AuthenticationManager中的ErrorController,我认为这是身份验证过程的一部分。在此过程完成之前,用户是未知的。

因此,您可以通过确保在调用ErrorController之前已知用户来解决您的问题,但真正的问题是为什么您需要ErrorController授权?最好的解决方案可能是确保ErrorController不需要授权。这将允许您在授权之前或期间发生错误时调用控制器。

您是否正在尝试从正在运行的应用程序调用此方法?还是从单元测试或除 MVC 运行时之外的任何其他内容 asp.net?如果第一个是真的,那么我能想到的没有什么可能导致 NRE 的,但如果第二个是真的,那么请确保 User 属性是具有值的提供者。

HttpContextBase GetContext(string userName) 
{
    var user = new GenericPrincipal(new GenericIdentity(userName, "Forms"), new string[] {});
    var contextMock = new Mock<HttpContextBase>();
    contextMock.Setup(context => context.User).Returns(user);
    contextMock.Setup.....
    return contextMock.Object;
}

我能够毫无例外地执行您的代码,尽管您情况下的确切情况可能会有所不同。

我不确定,如果你已经这样做了,如果你在Visual Studio中这样做,那么:

1)包装代码:controller.Execute(ctx);尝试...catch 块,以便您应该能够看到异常详细信息。您可以单击异常窗口底部的"查看详细信息",以便您可以在新窗口中查看完整的异常详细信息,例如InnerException,StackTrace等。它们可能会帮助您缩小例外范围。

2)在ErrorController的索引操作中放置一个断点,以便您可以通过按"F10"逐行执行检查:是否首先调用了ErrorController,并检查是否有任何代码抛出异常。

但是,您可以交叉检查的基本领域是:

1) "索引"操作中的代码 [您通过以下方式设置:

routeData.Values.Add("action", "Index"); ] 可能有一些可能正在抛出的未初始化对象。

2) 您从错误控制器的索引操作返回的视图存在于正确的视图文件夹中。

3) 如果错误视图按预期存在,则在视图中检查它是否未引用任何未初始化的对象。

如何确定请求上下文的哪个部分是问题所在 使"执行"抛出空引用异常?

  • 在执行调用线路上放置断点
  • 在Visual Studio中启用异常自动中断(调试 ->异常... ->公共语言运行时异常 ->系统 -> System.NullReferenceException ->"选中抛出的复选框")
  • 按 F5 继续执行程序并等待引发异常
  • 获取异常的堆栈跟踪或打开调用堆栈窗口(调试 -> 窗口 ->调用堆栈)
  • 从最顶层的调用中,您必须隔离未初始化的变量,您尝试对其执行操作。为此,您可以使用"自动"窗口("调试"-> 窗口>"自动")或"监视"窗口("调试"->"窗口"->"监视")来检查当前变量的内容(尽管不是lambdas)并查找哪个变量为空。