HttpErrors是自IIS 7.0以来可用的新功能,它在服务器级而不是应用程序级运行,并且更适合以有效的方式处理错误响应,例如通过使用当前响应而不是重定向.
但是,如果我认为ASP.NET中的这些工件的基础结构今天看起来给我们带来了一些问题.
一个简单的配置作为例子
<httpErrors existingResponse="Auto" errorMode="Custom"> <remove statusCode="404"/> <error statusCode="404" path="/Error/E404" responseMode="ExecuteURL" /> </httpErrors>
我们将errorMode设置为Custom,因为我们想测试错误处理本身,并将existingResponse设置为Auto,这将引入依赖于Response.TrySkipIisCustomErrors的分支:
> True:现有的错误响应将通过此模块未经处理,考虑到它的语义含义,这是完全合理的.
> False:如果HttpErrors模块具有匹配规则,则现有的错误响应将被替换为HttpErrors模块,这同样有意义.
理想情况下,这将允许我们自己处理一些错误,例如当../product/id中的产品不存在时,我们可以手动返回特定的404页面,其中包含有关缺失产品的信息,并且仍然让HttpErrors模块处理所有错误休息像../products/namebutshouldbeid或只是../misspelledandunmatchableurl.
但是,据我所知,这是行不通的.原因在于内部方法System.Web.HttpResponse.ReportRuntimeError,它将在运行时错误(例如未找到控制器/操作)上调用,并且我们有一个如下所示的部分:
// always try to disable IIS custom errors when we send an error if (_wr != null) { _wr.TrySkipIisCustomErrors = true; } if (!localExecute) { code = HttpException.GetHttpCodeForException(e); // Don't raise event for 404. See VSWhidbey 124147. if (code != 404) { WebBaseEvent.RaiseRuntimeError(e,this); } // This cannot use the HttpContext.IsCustomErrorEnabled property,since it must call // GetSettings() with the canThrow parameter. customErroRSSetting = CustomErroRSSection.GetSettings(_context,canThrow); if (customErroRSSetting != null) useCustomErrors = customErroRSSetting.CustomErrorsEnabled(Request); else useCustomErrors = true; }
在第一次调试时我发现useCustomErrors设置为false,我无法理解为什么因为我知道我有一个有效的HttpError配置,因为它可以在我从控制器返回HttpNotFoundResult的地方工作.
然后我意识到这不是HttpErrors,而是较旧的CustomErrors.而CustomErrors显然不知道HttpErrors.
错误”
所以发生的事情是Response.TrySkipIisCustomErrors设置为true,因为没有定义CustomErrors,它返回详细的404响应.此时我们希望HttpErrors启动,但它不会因为TrySkipIisCustomErrors现在设置为true.
我们也不能使用CustomErrors,因为这会让我们回到令人反感的错误重定向问题.
返回HttpNotFoundResult的原因是因为它不会触发运行时错误,只返回HttpErrors将按预期拦截的404结果,只要我们避免将Response.TrySkipIisCustomErrors设置为true即可.
应该如何处理/解决这个问题?
我认为默认情况下不允许System.Web.HttpResponse.ReportRuntimeError将Response.TrySkipIisCustomErrors设置为true,因为我们有另一个依赖于此的错误处理模块.因此,此方法也需要了解任何HttpErrors配置,或者通过避免将TrySkipIisCustomErrors设置为true(如果我们有任何CustomErrors配置),或者它与CustomErrors配置一起处理HttpErrors配置.
或者我错过了一些秘密魔法来解决这个问题?
解决方法
作为一种解决方法,我构建了一个能够将查询传输到ASP.NET MVC控制器的ASPX页面.
ErrorHandler.aspx.cs
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ErrorHandler.aspx.cs" Inherits="Guillaume.ErrorHandler" %>
代码背后
protected void Page_Load(object sender,EventArgs e) { //Get status code var queryStatusCode = Request.QueryString.Get("code"); int statusCode; if (!int.TryParse(queryStatusCode,out statusCode)) { var lastError = Server.GetLastError(); HttpException ex = lastError as HttpException; statusCode = ex == null ? 500 : ex.GetHttpCode(); } Response.StatusCode = statusCode; // Execute a route RouteData routeData = new RouteData(); string controllerName = Request.QueryString.Get("controller") ?? "Errors"; routeData.Values.Add("controller",controllerName); routeData.Values.Add("action",Request.QueryString.Get("action") ?? "Index"); var requestContext = new RequestContext(new HttpContextWrapper(Context),routeData); IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext,controllerName); controller.Execute(requestContext); }
在web.config中的用法
<configuration> <system.web> <customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="/Content/ErrorHandler.aspx"> <error statusCode="404" redirect="/Content/ErrorHandler.aspx?code=404&controller=Errors&action=NotFound" /> </customErrors> </system.web> </configuration>
常规浏览器请求的行为是正确的:当出现错误并返回错误代码时,将显示错误页面.
它在AJAX / Web API请求上也是正确的:不返回错误页面.我们在JSON或XML中得到可解析的错误.