gpt4 book ai didi

asp.net - CustomErrors 与 HttpErrors - 一个重大的设计缺陷?

转载 作者:行者123 更新时间:2023-12-02 10:06:55 28 4
gpt4 key购买 nike

正如我们所知,例如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设置为自定义,因为我们想测试错误处理本身,并将existingResponse设置为自动 em>,这将引入一个依赖于 Response.TrySkipIisCustomErrors 的分支:

  • 正确:现有的错误响应将未经处理地通过此模块,考虑到其语义,这是完全有意义的。
  • False:如果 HttpErrors 模块有匹配的规则,则现有的错误响应将被替换,这同样有意义。

理想情况下,这将允许我们自己处理一些错误,例如当 ../product/id 中的产品不存在时,我们可以手动返回包含有关缺失产品的信息的特定 404 页面,但仍然让 HttpErrors 模块处理所有其余内容,例如 ../products/namebutshouldbeid 或只是 ../misspelledandunmatchableurl

但是,据我所知,这不起作用。原因在于内部方法 System.Web.HttpResponse .ReportRuntimeError,它将在运行时错误(例如未找到 Controller /操作)时调用,并且我们有一个如下所示的部分:

// 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来自 Controller 。

然后我意识到这不是 HttpErrors,而是较旧的 CustomErrors。而且 CustomErrors 显然不知道 HttpErrors

“错误”

所以发生的事情是 Response.TrySkipIisCustomErrors设置为 true 并且由于没有定义 CustomErrors,因此它返回详细的 404 响应。此时,我们希望 HttpErrors 启动,但它不会启动,因为 TrySkipIisCustomErrors 现在设置为 true。

我们也不能使用 CustomErrors,因为这会让我们回到令人反感的错误重定向问题。

以及返回HttpNotFoundResult的原因之所以有效,是因为它不会触发运行时错误,只会返回 404 结果,HttpErrors 会按预期拦截,只要我们避免设置 Response.TrySkipIisCustomErrors为真。

应该/如何处理/解决这个问题?

我认为 System.Web.HttpResponse .ReportRuntimeError 不应被允许设置 Response.TrySkipIisCustomErrors默认情况下为 true,因为我们有另一个依赖于此的错误处理模块。因此,此方法需要了解任何 HttpErrors配置,或者通过避免将 TrySkipIisCustomErrors 设置为 true(如果我们有任何 CustomErrors)配置,或者它处理 HttpErrors 配置以及 CustomErrors 配置。

或者我错过了一些 secret 魔法来解决这个问题?

最佳答案

我已经努力解决这个问题好几天了,我认为唯一有效的解决方案是发布的 here (Starain chen 对 @Alex 在 forums.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();
}
}

创建 View

ErrorController中的操作创建 View 。 (403.cshtml404.cshtml500.cshtmlGeneral.cshtml)

为什么我认为这是唯一有效的解决方案?

  1. 它可以处理 ASP.NET MVC 和 IIS 级别的错误(假设使用 IIS7+ 和集成管道)
  2. 它返回有效的 http 状态代码。 (不是 302 或 200)
  3. 如果我直接导​​航到错误页面,它会返回 200 OK:如果我导航到 /error/404<,我希望得到 200 OK/
  4. 我可以调整错误页面内容。 (使用ViewBag.ErrorMessage)
  5. 如果客户端发出错误的 AJAX 请求并需要 json 数据(在 Controller 的操作内),他/她将获得带有适当状态代码的 json 数据。

关于asp.net - CustomErrors 与 HttpErrors - 一个重大的设计缺陷?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24465261/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com