我们可能知道,例如, What is the difference between customErrors and httpErrors?,CustomErrors是一种在Web应用程序中定义错误页面的旧方法,但这种方法存在一些问题 - 例如,如果您关心正确的http响应代码,因为CustomErrors的方法是重定向到错误页面而不是替换当前响应,这会通过http状态代码破坏通信的大部分语义完整性。
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的分支:
这理想情况下允许我们自己处理一些错误,例如当 ../ 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 。
&#34;错误&#34;
所以发生的事情是Response.TrySkipIisCustomErrors设置为true,并且由于没有定义 CustomErrors ,它会返回详细的404响应。此时我们希望 HttpErrors 启动,但它不会因为 TrySkipIisCustomErrors 现在设置为true。
我们也无法使用 CustomErrors ,因为这会让我们回到令人反感的错误重定向问题。
返回HttpNotFoundResult的原因是因为它不会触发运行时错误,只返回一个404结果 HttpErrors 将按预期拦截,只要我们避免将Response.TrySkipIisCustomErrors设为true。
应如何处理/解决此问题?
我认为默认情况下不允许 System.Web.HttpResponse。ReportRuntimeError 将Response.TrySkipIisCustomErrors设置为true,因为我们有另一个依赖于此的错误处理模块。因此,如果我们有任何HttpErrors配置,或者它处理< em> HttpErrors 配置以及 CustomErrors 配置。
或者我错过了一些秘密魔法来解决这个问题?
答案 0 :(得分:2)
就像你一样,我认为ASP.NET不应该设置TrySkipIisCustomErrors或者可以添加一个选项,以便我们可以避免它。
作为一种解决方法,我构建了一个能够将查询传输到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中得到了一个可解析的错误。
如果您希望将非ASP.NET错误重定向到自定义错误,则可以添加httpErrors
部分。
答案 1 :(得分:2)
我一直在努力解决这个问题,我认为唯一有效的解决方案是here {Starain chen对@Alex在论坛上发布的同一问题的回答here .asp.net):
(我稍微修改过代码)
public class CustomHandleErrorAttribute : HandleErrorAttribute {
public override void OnException (ExceptionContext filterContext) {
if (filterContext.ExceptionHandled) {
return;
}
var httpException = new HttpException(null, filterContext.Exception);
var httpStatusCode = httpException.GetHttpCode();
switch ((HttpStatusCode) httpStatusCode) {
case HttpStatusCode.Forbidden:
case HttpStatusCode.NotFound:
case HttpStatusCode.InternalServerError:
break;
default:
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception)) {
return;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest") {
filterContext.Result = new JsonResult {
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new {
error = true,
message = filterContext.Exception.Message
}
};
}
else {
var controllerName = (String) filterContext.RouteData.Values["controller"];
var actionName = (String) filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult {
ViewName = String.Format("~/Views/Hata/{0}.cshtml", httpStatusCode),
ViewData = new ViewDataDictionary(model),
TempData = filterContext.Controller.TempData
};
}
// TODO: Log the error by using your own method
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = httpStatusCode;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
App_Start/FilterConfig.cs
public class FilterConfig {
public static void RegisterGlobalFilters (GlobalFilterCollection filters) {
filters.Add(new CustomHandleErrorAttribute());
}
}
Global.asax
protected void Application_Error () {
var exception = Server.GetLastError();
var httpException = exception as HttpException ?? new HttpException((Int32) HttpStatusCode.InternalServerError, "Internal Server Error", exception);
var httpStatusCode = httpException.GetHttpCode();
Response.Clear();
var routeData = new RouteData();
routeData.Values.Add("Controller", "Error");
routeData.Values.Add("fromAppErrorEvent", true);
routeData.Values.Add("ErrorMessage", httpException.Message);
routeData.Values.Add("HttpStatusCode", httpStatusCode);
switch ((HttpStatusCode) httpStatusCode) {
case HttpStatusCode.Forbidden:
case HttpStatusCode.NotFound:
case HttpStatusCode.InternalServerError:
routeData.Values.Add("action", httpStatusCode.ToString());
break;
default:
routeData.Values.Add("action", "General");
break;
}
Server.ClearError();
IController controller = new Controllers.ErrorController();
// TODO: Log the error if you like
controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
ErrorController
[AllowAnonymous]
public class ErrorController : Controller {
protected override void OnActionExecuting (ActionExecutingContext filterContext) {
base.OnActionExecuting(filterContext);
var errorMessage = RouteData.Values["ErrorMessage"];
var httpStatusCode = RouteData.Values["HttpStatusCode"];
if (errorMessage != null) {
ViewBag.ErrorMessage = (String) errorMessage;
}
if (httpStatusCode != null) {
ViewBag.HttpStatusCode = Response.StatusCode = (Int32) httpStatusCode;
}
Response.TrySkipIisCustomErrors = true;
}
[ActionName("403")]
public ActionResult Error403 () {
return View();
}
[ActionName("404")]
public ActionResult Error404 () {
return View();
}
[ActionName("500")]
public ActionResult Error500 () {
return View();
}
public ActionResult General () {
return View();
}
}
为ErrorController
中的操作创建视图。 (403.cshtml
,404.cshtml
,500.cshtml
和General.cshtml
)
200 OK
:如果我导航到200 OK
,我希望获得/error/404
。ViewBag.ErrorMessage
)