通过responseMode ="ExecuteURL"清除HttpContext.Current.Items?

sha*_*non 5 asp.net asp.net-mvc elmah httpcontext server.transfer

我避免使用默认的ASP.NET方法来重定向错误(就像许多人一样).清洁AJAX代码和SEO是原因之一.

但是,我正在使用以下方法来执行此操作,似乎我可能会HttpContext.Current.Items在转移中丢失?

<httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="401" />
    <remove statusCode="403" />
    <remove statusCode="404" />
    <remove statusCode="500" />
    <error statusCode="401" responseMode="ExecuteURL" path="/Account/SignIn" />
    <error statusCode="403" responseMode="ExecuteURL" path="/Site/Forbidden" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Site/NotFound" />
    <error statusCode="500" responseMode="ExecuteURL" path="/Site/Error" />
</httpErrors>
Run Code Online (Sandbox Code Playgroud)

我认为它只是执行了一个Server.Transfer()封面,我理解保留Items.(参见:HttpContext.Current.Items的范围http://weblog.west-wind.com/posts/2010/Jan/20/HttpContextItems-and-ServerTransferExecute)

但是我也在Items"ExecuteURL"之前捕获了一些内容,并在转移之后检索/输出它(或者它是什么),它似乎消失了.我已经看到它进入Items集合,我看到Count加注到5,然后当检索到值时,集合中只有2个项目.

到底是怎么回事?


如果你想更多地了解我正在做的事情并推荐一个替代实现,我愿意接受它.我正在使用它以一种没有竞争条件的方式将ELMAH Error Id推送到ViewModel中.(即我正在替换的常见解决方法是仅显示最近的错误.)这是我的代码:

Global.asax中

protected void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args) {
    ElmahSupplement.CurrentId = args.Entry.Id;
}

void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e) {
    if (ElmahSupplement.IsNotFound(e.Exception)) {
        ElmahSupplement.LogNotFound((e.Context as HttpContext).Request);
        e.Dismiss();
    }
}
Run Code Online (Sandbox Code Playgroud)

SiteController.cs

public virtual ActionResult Error() {
    Response.StatusCode = 500;
    return View(MVC.Site.Views.Error, ElmahSupplement.CurrentId);
}
Run Code Online (Sandbox Code Playgroud)

ElmahSupplement.cs

public class ElmahSupplement {
    // TODO: This is a rather fragile way to access this info
    private static readonly Guid contextId = new Guid("A41A67AA-8966-4205-B6C1-14128A653F21");

    public static string CurrentId {
        get { 
            return
                // Elmah 1.2 will fail to log when enumerating form values that raise RequestValidationException (angle brackets)
                // https://code.google.com/p/elmah/issues/detail?id=217
                // So this id could technically be empty here
                (HttpContext.Current.Items[contextId] as string);
        }
        set {
            HttpContext.Current.Items[contextId] = value;
        }
    }

    public static void LogNotFound(HttpRequest request) {
        var context = RepositoryProxy.Context;
        context.NotFoundErrors.Add(new NotFoundError {
            RecordedOn = DateTime.UtcNow,
            Url = request.Url.ToString(),
            ClientAddress = request.UserHostAddress,
            Referrer = request.UrlReferrer == null ? "" : request.UrlReferrer.ToString()
        });
        context.SaveChanges();
    }

    public static bool IsNotFound(Exception e) {
        HttpException he = e as HttpException;
        return he != null && he.GetHttpCode() == 404;
    }
}
Run Code Online (Sandbox Code Playgroud)

sha*_*non 0

我跟踪了踪迹并确定了以下内容。有些是松散推断的。

CustomErrorModule(在 IIS 模块堆栈中)接收 SEND_RESPONSE 通知。

HttpStatus 为 500,因此它克隆上下文,设置新的 URL(根据匹配的自定义错误规则),并在此上下文上执行请求(请参阅ExecuteRequest)。

每个文档的目的HttpContext.Items是:

获取一个键/值集合,可用于在 HTTP 请求期间在 IHttpModule 接口和 IHttpHandler 接口之间组织和共享数据。

批判性地看这个函数定义,当然只有“HTTP请求”。但是,字典本身似乎Items是以 HttpContext 为键的字典中的一项,而 HttpContext 是此执行子请求中的唯一(克隆)引用。跟踪显示为此运行的完整管道(所有模块,例如重复身份验证)ExecuteURL,因此当然需要这个隔离的上下文。

从非托管代码来看,它对于GetParentContext来说是微不足道的。但是,从托管代码中,此层次结构不可用。所以,我没有办法检索原始的Items.

作为替代解决方案,利用 Global.asax 变量可能会起作用,因为我的测试显示子请求共享一个ApplicationInstance,但我不确定客户端对此的访问是否一定是顺序的。

另一种可能更好的方法是避免重新运行整个管道;永远不要退出 MVC 处理程序(例如Controller.OnExceptionTransferToAction 。但是,这会阻止为错误页面配置实现单点事实,因为错误也可能在 MVC 感知之外出现。