【问题标题】:HttpContext.Current.Items cleared via responseMode="ExecuteURL"?HttpContext.Current.Items 通过 responseMode="ExecuteURL" 清除?
【发布时间】:2014-03-26 13:59:49
【问题描述】:

我避免使用默认的 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>

我以为它只是在幕后执行了Server.Transfer(),据我所知保留了Items。 (参见:Scope of HttpContext.Current.Itemshttp://weblog.west-wind.com/posts/2010/Jan/20/HttpContextItems-and-ServerTransferExecute

但我还在“ExecuteURL”之前的Items 中捕获了一些内容,并在传输(或其他任何内容)之后检索/输出它,它似乎消失了。我看到它进入了Items 集合,我看到Count 提高到5,然后当检索到该值时,集合中只有2 个项目。

发生了什么事?


如果您想更多地了解我在做什么并推荐一个替代实现,我愿意接受。我正在使用它以一种不受竞争条件的方式将 ELMAH 错误 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();
    }
}

SiteController.cs

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

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;
    }
}

【问题讨论】:

  • 您的假设是错误的,ASP.NET MVC 不再使用 Server.Transfer,因此它的行为不像您预期​​的那样。 Server.Transfer 也不适用于 ASP.NET MVC,因为 MVC 是在异步管道上重建的。
  • Akash,你好像在做两个 cmets,但我理解他们说的是同一件事,所以也许我误解了?另外,虽然我当然相信你,但我并不真正理解 Server.Transfer 的问题。这不是在 MVC 处理程序的范围之外执行的吗?我想最重要的是,如果您对如何解决有任何建议,请提出。 18 小时,直到 +50 丢失。 :(

标签: asp.net asp.net-mvc elmah httpcontext server.transfer


【解决方案1】:

正如here 解释的那样,ExecuteURL 生成两个请求:第一个引发异常,第二个生成错误响应。

由于 Context.Items 在请求之间被清除,您的代码总是会看到生成的第二个请求,因此项目之间的差异。

尝试帖子中的建议:改用 system.web > customErrors 和 redirectMode="ResponseRewrite"。

【讨论】:

  • 恐怕这并不能回答为什么上下文正在清除。如果实际上实现了第二个“虚拟”客户端请求,那么机制是什么?所以我仍然没有足够的信息来解决它。如果建议的解决方法适用,我仍然会接受答案,但它不适用于 MVC。感谢您提供链接(+1)。我来看看踪迹。
  • 我应该澄清一下,由于 context Items 的主要功能是在处理程序和模块之间共享信息,第二个请求必须在流程的最开始重新开始才能丢失此信息(HttpContext) 自然。同时,响应的 IP 也会丢失。
  • 从我的测试来看,CustomErrorModule 的 ExecuteURL 似乎生成了一个子请求,在克隆的 HttpContext 上运行。它运行整个管道,由先前的上下文包装。但是,由于此子上下文是在非托管代码中克隆的,因此它不会收到 Items 的副本。
【解决方案2】:

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

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

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

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

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

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

从非托管代码到GetParentContext 是微不足道的。但是,从托管代码中,此层次结构不可用。所以,我无法找回原来的Items

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

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

【讨论】:

    猜你喜欢
    • 2012-06-10
    • 2012-04-22
    • 1970-01-01
    • 2019-12-11
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多