设置redirectMode =“ ResponseRewrite”时,CustomErrors不起作用


73

在一个旧站点中,我通过添加redirectMode="ResponseRewrite"(3.5 SP1中的新增功能)来更改CustomErrors的工作方式:

<customErrors mode="RemoteOnly" defaultRedirect="Error.aspx" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
</customErrors> 

关键是:它向我显示了通用错误页面(未设置时会显示该页面customErrors。如果我删除了该redirectMode="ResponseRewrite"部件,它将正常工作。

我确定服务器上安装了3.5 SP1,因为我在同一服务器上托管的其他站点上使用了相同的设置。

有任何想法吗?

Answers:


102

对于在幕后ResponseRewrite使用的MVC应用程序中尝试执行此操作的任何人,请务必注意Server.Transfer。因此,defaultRedirect必须与文件系统上的合法文件相对应。显然,Server.Transfer它与MVC路由不兼容,因此,如果您的错误页面由控制器操作提供服务,Server.Transfer它将查找/ Error / Whatever,在文件系统上找不到它,并返回通用的404错误页面!


我注意到一个问题,还与Web窗体和路由在这里看到我的问题stackoverflow.com/questions/7269103/...
GibboK

有一个CodePlex问题,允许ResponseRewrite用于MVC路由,请投票:aspnet.codeplex.com/workitem/9034
Dmitry

1
@PussInBoots,这里有一个存档链接web.archive.org/web/20131201222548/http://aspnet.codeplex.com/...
KyleMit

51

对我而言,唯一有效的方法是关闭自定义错误,并通过web.config替换iis的错误页面。它使用响应发送正确的状态代码,并且具有不通过mvc的好处。

这是代码

  1. 关闭自定义错误

    <customErrors mode="Off" />
    
  2. 替换错误页面

    <httpErrors errorMode="Custom" existingResponse="Replace">
      <remove statusCode="404" subStatusCode="-1" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="404" path="Error404.html" responseMode="File" />
      <error statusCode="500" path="Error.html" responseMode="File" />
    </httpErrors>
    

注意。使用responsemode="file"如果URL是直接链接到一个文件

信息:http : //tipila.com/tips/use-custom-error-pages-aspnet-mvc


请参阅meta.stackexchange.com/a/22189/147333,以了解列表中的代码格式:)
CharlesB 2012年

3
我发疯了,试图为我的应用程序找到处理错误的“正确”方法,这就是解决方案。我仍然需要处理全局错误,但是可以,因为我希望记录这些错误。发生这种情况时,我正在执行Server.Transfer到aspx错误页面。该解决方案的最大优势在于,用户永远不会知道我的处理程序的名称,也永远不会进入他们未请求的URL。
克里斯·波特

2
该解决方案是远远不够完善,它将取代AJAX和API错误响应stackoverflow.com/questions/24465261/...
纪尧姆

我设置了existResponse =“ Auto”会导致各种各样的问题,因为它可以处理404错误,但不能解决500个错误。谢谢!如果要删除所有以前的httpError,我发现您可以使用<clear />而不是单独的<remove ... />我也发现我无法将500个错误传递给控制器​​并像我一样设置responseMode = ExecuteURL适用于404错误。
Peheje

20

IIS正在捕获错误状态代码,并显示它自己的错误页面,而不是您自己的错误页面。要解决此问题,需要在错误页面的后面代码中对此进行设置,以防止IIS这样做:

Response.TrySkipIisCustomErrors = true;

这仅适用于IIS7或更高版本,对于IIS的早期版本,您需要使用错误页面设置。


2
谢谢-如果您需要使用.aspx页作为defaultRedirect,这是解决方案。
frankadelic

1
感谢您告诉我有关TrySkipIisCustomErrors的信息。我必须切换回ResponseRedirect,因为我们必须坚持使用IIS 6 ...
Vinz 2010年

我已经增加了大约TrySkipIisCustomErrors多一点信息在这个答案stackoverflow.com/a/21271085/222748
迈克尔

16

由于依赖,Server.Transfer似乎的内部实现ResponseRewrite与MVC不兼容。

这对我来说似乎是一个巨大的功能漏洞,因此我决定使用HTTP模块重新实现此功能,以便它可以正常工作。下面的解决方案使您能够像平常一样通过重定向到任何有效的MVC路由(包括物理文件)来处理错误。

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
    <error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>

已在以下平台上进行了测试;

  • 集成管道模式下的MVC4(IIS Express 8)
  • 经典模式下的MVC4(VS开发服务器,Cassini)
  • 经典模式下的MVC4(IIS6)

namespace Foo.Bar.Modules {

    /// <summary>
    /// Enables support for CustomErrors ResponseRewrite mode in MVC.
    /// </summary>
    public class ErrorHandler : IHttpModule {

        private HttpContext HttpContext { get { return HttpContext.Current; } }
        private CustomErrorsSection CustomErrors { get; set; }

        public void Init(HttpApplication application) {
            System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
            CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");

            application.EndRequest += Application_EndRequest;
        }

        protected void Application_EndRequest(object sender, EventArgs e) {

            // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
            if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {

                int statusCode = HttpContext.Response.StatusCode;

                // if this request has thrown an exception then find the real status code
                Exception exception = HttpContext.Error;
                if (exception != null) {
                    // set default error status code for application exceptions
                    statusCode = (int)HttpStatusCode.InternalServerError;
                }

                HttpException httpException = exception as HttpException;
                if (httpException != null) {
                    statusCode = httpException.GetHttpCode();
                }

                if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {

                    Dictionary<int, string> errorPaths = new Dictionary<int, string>();

                    foreach (CustomError error in CustomErrors.Errors) {
                        errorPaths.Add(error.StatusCode, error.Redirect);
                    }

                    // find a custom error path for this status code
                    if (errorPaths.Keys.Contains(statusCode)) {
                        string url = errorPaths[statusCode];

                        // avoid circular redirects
                        if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {

                            HttpContext.Response.Clear();
                            HttpContext.Response.TrySkipIisCustomErrors = true;

                            HttpContext.Server.ClearError();

                            // do the redirect here
                            if (HttpRuntime.UsingIntegratedPipeline) {
                                HttpContext.Server.TransferRequest(url, true);
                            }
                            else {
                                HttpContext.RewritePath(url, false);

                                IHttpHandler httpHandler = new MvcHttpHandler();
                                httpHandler.ProcessRequest(HttpContext);
                            }

                            // return the original status code to the client
                            // (this won't work in integrated pipleline mode)
                            HttpContext.Response.StatusCode = statusCode;

                        }
                    }

                }

            }

        }

        public void Dispose() {

        }


    }

}

用法

将其作为web.config中的最终HTTP模块

  <system.web>
    <httpModules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </httpModules>
  </system.web>

  <!-- IIS7+ -->
  <system.webServer>
    <modules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </modules>
  </system.webServer>

谢谢您的解决方案。我已经将代码放置到global.asax,但是状态仍然是200。要解决集成pipleine的问题,我们需要更早设置状态。因此,我能够将它放在404页面操作中:[AllowAnonymous] public ActionResult NotFound(){Response.StatusCode = 404; 返回View(“ NotFound”); }
Pavel Korsukov 2015年

我一直在愉快地使用此方法,但最近发现它会干扰IIS应用程序的初始化。还没有机会深入研究它,但是当通过web.config加载错误处理程序时,它似乎优先于初始化过程。结果,当尝试结合使用该remapManagedRequestsTo属性时,skipManagedModules="true"在重新映射页面加载之前可能会有很大的延迟。我可以通过应用程序初始化加载模块来解决此问题。
BrianS

10

我知道这个问题有点老了,但是我想指出一点,它不需要是静态文件即可运行。

我遇到了类似的问题,这只是在您的Error.aspx中发现该错误的问题,在我们的情况下,这是因为所使用的母版页依赖于部分会话数据,并且在设置ResponseRewrite时,该会话不可用我们的Error.aspx页面。

我还没有确定会话的不可用是由于我们特定的应用程序配置还是ASP.net的“按设计”部分。


是的,你是对的。就我而言,找不到错误,因此使用静态HTML可以确保安全。
爱德华多·莫尔蒂尼

1

我发现问题出在Error.aspx中。仍然找不到导致问题的error.aspx中的实际错误。

将页面更改为静态html文件即可解决此问题。


1

我在aspx中建立了一个错误页面,该页面将查询转移到ASP.NET MVC控制器。您可以将查询重写到此aspx页面,它将把查询转移到您的自定义控制器。

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

在此处查找更多详细信息:https : //stackoverflow.com/a/27354140/143503


我将此处提供的解决方案与stackoverflow.com/a/5536676/2310818结合使用,以解决各种错误,无论是由于未知路由,未知控制器,未知操作,控制器操作返回的HttpNotFound()结果以及引发的HttpException导致的由控制器。我获得了所有这些,同时根据错误代码的类型获得了正确的状态代码(404、500)。
Parth Shah 2015年

1
@ParthShah,它单独使用此解决方案,但可能对返回错误的方式有一些限制(抛出异常而不是返回我猜是但我不太记得的结果)。使用ASP MVC / Web API和IIS进行错误处理很痛苦,很高兴您设法使它起作用;)
Guillaume

0

在我的特定情况下,我的错误页面有一个母版页,该母版页具有一个试图使用Session的用户控件。如果Session不可用,则会得到HttpException:“仅在配置文件或Page指令中将enableSessionState设置为true时,才能使用会话状态。” 最简单的解决方法是切换到静态html,第二最简单的解决方法是使用简单的错误页面,最难的解决方法是难以置信地确保您的错误页面在任何地方都没有任何假设(例如,Session不会引发异常),并且不可能出错。


0

我发现,如果您使用redirectMode =“ ResponseRewrite”,则需要在web.config文件的重写区域中添加一些内容。问题是您的网站损坏了!您无法URL重写,因为您的站点无法调用处理您重写的“ virtual.aspx”!


0

根据@Amila的帖子以及该帖子的确认和完成,我有同样的问题,我在谷歌上挖了很多东西,但没有机会找到正确的答案。问题是,当您使用时ASP.Net Web Application,无论MVC使用还是旧方法,都无法实现自定义错误Webform project
如果您使用的是此选项ASP.Net Web Application(是否使用MVC):

在我的场景中,我只想为特定的404错误定义一个自定义错误,另一个错误定义为与404错误相同:


Senario1:您的自定义页面是一个简单HTML文件,放在以下位置root

<configuration>
   <system.web>
      <customErrors mode="Off" />
   </system.web>
   <system.webServer>
       <httpErrors errorMode="Custom" existingResponse="Replace">
           <remove statusCode="404" subStatusCode="-1" />
           <error statusCode="404" path="ErrorPage.html" responseMode="File" />
       </httpErrors>
   </system.webServer>
</configuration>



Senario2:您的自定义页面是一个aspx页面,位于以下位置root

<configuration>
   <system.web>
      <customErrors mode="Off" />
   </system.web>
   <system.webServer>
       <httpErrors errorMode="Custom" existingResponse="Replace">
           <remove statusCode="404" subStatusCode="-1" />
           <error statusCode="404" path="ErrorPage" responseMode="Redirect" />
       </httpErrors>
   </system.webServer>
</configuration>

注:我取下ASPX扩展,由于RouteConfig.csASP.net application,你可以使用ErrorPage.aspx,如果你喜欢,它是可选的。


Senario3:您的自定义页面是一个aspx页面,放在以下页面中[ex: Page folder in The root (~/Page/ErrorPage.aspx)]
我注意到的提示是:您不应该使用~/root寻址;所以我只是没有~/标记地添加:

<configuration>
   <system.web>
      <customErrors mode="Off" />
   </system.web>
   <system.webServer>
       <httpErrors errorMode="Custom" existingResponse="Replace">
           <remove statusCode="404" subStatusCode="-1" />
           <error statusCode="404" path="Page/ErrorPage" responseMode="Redirect" />
       </httpErrors>
   </system.webServer>
</configuration>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.