我避免使用默认的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.Items和http://weblog.west-wind.com/posts/2010/Jan/20/HttpContextItems-and-ServerTransferExecute)
但我也在“ExecuteURL”之前在Items
中捕捉一些东西,并在转移之后检索/输出它(或者不管它是什么),它似乎消失了。我看到它进入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();
}
}
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;
}
}
答案 0 :(得分:1)
正如here所述,ExecuteURL生成两个请求:第一个抛出异常,第二个生成错误响应。
由于Context.Items在请求之间被清除,因此您的代码始终会看到生成的第二个请求,因此项目之间存在差异。
尝试帖子中的消息:使用system.web&gt; customErrors with redirectMode =&#34; ResponseRewrite&#34;代替。
答案 1 :(得分:0)
我跟踪了一下并确定了以下内容。有些是松散推断的。
CustomErrorModule(在IIS模块堆栈中)接收SEND_RESPONSE通知。
HttpStatus是500,因此它克隆上下文,设置新的URL(根据匹配的自定义错误规则),并在此上下文上执行请求(请参阅ExecuteRequest)。
每个文档HttpContext.Items
的目的是:
获取可用于组织和共享的键/值集合 IHttpModule接口和IHttpHandler接口之间的数据 在HTTP请求期间。
当然,批判性地查看此功能定义只有&#34; HTTP请求&#34;。但是,Items
字典本身可能是键入HttpContext的字典中的项目,HttpContext是此执行子请求中的唯一(克隆)引用。跟踪显示了为此ExecuteURL
运行的完整管道(所有模块,例如重复身份验证),因此当然需要此隔离的上下文。
从非托管代码来看,GetParentContext是微不足道的。但是,从托管代码中,此层次结构不可用。所以,我没有办法找回原来的Items
。
作为替代解决方案,利用Global.asax变量可能是有用的,因为我的测试显示子请求共享ApplicationInstance
,但我不确定客户端对此的访问必然是顺序的
另一种可能更好的方法是避免重新运行整个管道;永远不要退出MVC处理程序(例如Controller.OnException
和TransferToAction)。但是,这可以防止为错误页面配置实现单点真理,因为错误也可以在MVC的识别之外引发。