CustomErrors funktioniert nicht, wenn redirectMode = "ResponseRewrite" gesetzt wird.

72

Auf einer alten Site habe ich die Funktionsweise von CustomErrors durch Hinzufügen geändert redirectMode="ResponseRewrite"(neu in 3.5 SP1):

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

Die Sache ist: Es zeigt mir die allgemeine Fehlerseite (die, die Sie erhalten, wenn Sie nicht festlegen customErrors. Wenn ich das redirectMode="ResponseRewrite"Teil entferne , funktioniert es einwandfrei.

Ich bin sicher, dass 3.5 SP1 auf dem Server installiert ist, da ich auf anderen Sites, die auf demselben Server gehostet werden, dieselbe Einstellung verwende.

Irgendwelche Ideen?

Eduardo Molteni
quelle

Antworten:

102

Es ist wichtig zu beachten, dass jeder, der dies versucht, dies in einer MVC-Anwendung tut, ResponseRewritedie Server.Transferhinter den Kulissen verwendet. Daher defaultRedirectmuss das einer legitimen Datei im Dateisystem entsprechen. Anscheinend Server.Transferist es nicht mit MVC-Routen kompatibel. Wenn Ihre Fehlerseite von einer Controller-Aktion bedient wird, Server.Transferwird nach / Error / Whatever gesucht, nicht im Dateisystem gefunden und eine generische 404-Fehlerseite zurückgegeben!

Michael Hallock
quelle
Ich bemerke
GibboK
Es gibt ein CodePlex-Problem, das es ResponseRewrite ermöglicht, mit MVC-Routen zu arbeiten. Bitte stimmen Sie ab: aspnet.codeplex.com/workitem/9034
Dmitry
1
@PussInBoots, hier ist ein archivierter Link web.archive.org/web/20131201222548/http://aspnet.codeplex.com/…
KyleMit
50

Die einzige Möglichkeit, die für mich perfekt funktioniert hat, besteht darin, benutzerdefinierte Fehler zu deaktivieren und die Fehlerseiten von iis über web.config zu ersetzen. Es sendet den richtigen Statuscode mit der Antwort und hat den Vorteil, dass es nicht durch den MVC geht.

Hier ist der Code

  1. Deaktivieren Sie benutzerdefinierte Fehler

    <customErrors mode="Off" />
    
  2. Ersetzen Sie die Fehlerseiten

    <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>
    

Hinweis. Verwenden Sie responsemode="file"diese Option, wenn die URL ein direkter Link zu einer Datei ist

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

Amila
quelle
Siehe meta.stackexchange.com/a/22189/147333 für die Code-Formatierung in einer Liste :)
CharlesB
3
Ich habe mich wahnsinnig gemacht und versucht, den "richtigen" Weg zu finden, um mit Fehlern für meine Anwendung umzugehen, und dies war die Lösung. Ich muss noch einige Fehler in der globalen behandeln, aber das ist in Ordnung, weil ich möchte, dass diese protokolliert werden. In diesem Fall mache ich einen Server. Übertragen Sie auf eine Aspx-Fehlerseite. Der größte Teil dieser Lösung besteht darin, dass der Benutzer nie weiß, wie mein Handler heißt, und niemals zu einer URL weitergeleitet wird, die er nicht angefordert hat.
Chris Porter
2
Diese Lösung ist alles andere als
Guillaume
Ich hatte existierende Antwort = "Auto" gesetzt, was alle möglichen Probleme verursachte, da es mit 404, aber nicht 500 Fehlern funktionierte. Vielen Dank! Wenn Sie alle vorherigen httpErrors entfernen möchten, können Sie <clear /> anstelle von individual <remove ... /> verwenden. Außerdem konnte ich keine 500 Fehler an einen Controller übergeben und responseMode = ExecuteURL wie bisher festlegen für 404 Fehler, die funktionierten.
Peheje
20

Was passiert ist, dass IIS den Fehlerstatuscode erkennt und anstelle Ihrer eigenen eine eigene Fehlerseite anzeigt. Um dies zu lösen, müssen Sie dies im Code hinter der Seite Ihrer Fehlerseite festlegen, um zu verhindern, dass IIS dies tut:

Response.TrySkipIisCustomErrors = true;

Dies funktioniert nur in IIS7 oder höher. In früheren Versionen von IIS müssen Sie mit den Einstellungen der Fehlerseite spielen.

Michael
quelle
2
Vielen Dank - dies ist die Lösung, wenn Sie eine ASPX-Seite als defaultRedirect verwenden müssen.
Frankadelic
1
Vielen Dank, dass Sie mir von TrySkipIisCustomErrors erzählt haben. Ich muss zurück zu ResponseRedirect wechseln, da wir uns aber an IIS 6 halten müssen ...
Vinz
Ich habe ein bisschen mehr Informationen über TrySkipIisCustomErrors in dieser Antwort hinzugefügt. Stackoverflow.com/a/21271085/222748
Michael
16

Aufgrund des Vertrauens Server.Transferscheint die interne Implementierung von ResponseRewritenicht mit MVC kompatibel zu sein.

Dies scheint mir eine eklatante Funktionslücke zu sein, daher habe ich beschlossen, diese Funktion mithilfe eines HTTP-Moduls erneut zu implementieren, damit sie einfach funktioniert . Mit der folgenden Lösung können Sie Fehler behandeln, indem Sie wie gewohnt zu einer gültigen MVC-Route (einschließlich physischer Dateien) umleiten.

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

Dies wurde auf den folgenden Plattformen getestet.

  • MVC4 im integrierten Pipeline-Modus (IIS Express 8)
  • MVC4 im klassischen Modus (VS Development Server, Cassini)
  • MVC4 im klassischen Modus (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() {

        }


    }

}

Verwendung

Fügen Sie dies als letztes HTTP-Modul in Ihre web.config ein

  <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>
Red Taz
quelle
Vielen Dank für die Lösung. Ich habe den Code auf global.asax gesetzt, aber der Status war immer noch 200. Um das Problem mit der integrierten Pipleine zu lösen, müssen wir den Status früher festlegen. Also konnte ich es in eine 404-Seiten-Aktion einfügen: [AllowAnonymous] public ActionResult NotFound () {Response.StatusCode = 404; return View ("NotFound"); }
Pavel Korsukov
Ich habe diese Methode gerne verwendet, aber kürzlich festgestellt, dass sie die Initialisierung von IIS-Anwendungen beeinträchtigen kann. Ich hatte noch keine Gelegenheit, mich damit zu beschäftigen, aber wenn der Fehlerhandler über web.config geladen wird, scheint er Vorrang vor dem Initialisierungsprozess zu haben. Wenn Sie versuchen, das remapManagedRequestsToAttribut in Kombination mit skipManagedModules="true"zu verwenden, kann es daher zu einer erheblichen Verzögerung kommen, bevor die Remap-Seite geladen wird. Ich konnte das Problem umgehen, indem ich das Modul über die App-Initialisierung lud .
BrianS
10

Ich weiß, dass diese Frage etwas alt ist, aber ich dachte, ich sollte darauf hinweisen, dass es keine statische Datei sein muss, damit dies funktioniert.

Ich bin auf eine ähnliche Sache gestoßen, und es geht nur darum, diesen Fehler in Ihrer Error.aspx zu finden. In unserem Fall lag dies daran, dass die verwendete Masterseite auf Sitzungsdaten beruhte und die Sitzung nicht verfügbar war, wenn ResponseRewrite festgelegt wurde unsere Error.aspx Seite.

Ich habe noch nicht herausgefunden, ob diese Nichtverfügbarkeit der Sitzung auf unsere spezifische App-Konfiguration oder einen "by Design" -Teil von ASP.net zurückzuführen ist.

Chris
quelle
Ja, du hast recht. In meinem Fall konnte ich den Fehler nicht finden, daher gehe ich mit einem statischen HTML-Code auf Nummer sicher.
Eduardo Molteni
1

Ich fand, dass das Problem in Error.aspx war. Ich kann immer noch nicht finden, was der eigentliche Fehler in error.aspx war, der das Problem verursacht.

Das Ändern der Seite in eine statische HTML-Datei löste das Problem.

Eduardo Molteni
quelle
1

Ich habe in aspx eine Fehlerseite erstellt, die die Abfrage an einen ASP.NET MVC-Controller überträgt. Sie können die Abfrage auf diese Aspx-Seite umschreiben und die Abfrage wird auf Ihren benutzerdefinierten Controller übertragen.

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

Weitere Details finden Sie hier: https://stackoverflow.com/a/27354140/143503

Guillaume
quelle
Ich habe die hier angebotene Lösung mit stackoverflow.com/a/5536676/2310818 kombiniert , um alle Arten von Fehlern zu beheben, die durch unbekannte Route, unbekannten Controller, unbekannte Aktion, von der Controller-Aktion zurückgegebenes HttpNotFound () -Ergebnis und ausgelöste HttpException verursacht wurden von einem Controller. All dies habe ich erreicht, während ich je nach Art des Fehlercodes die richtigen Statuscodes (404, 500) erhalten habe.
Parth Shah
1
@ParthShah, es hat nur mit dieser Lösung funktioniert, aber vielleicht mit einer gewissen Einschränkung, wie Sie Fehler zurückgeben (eine Ausnahme auslösen, anstatt ein Ergebnis zurückzugeben, denke ich, aber ich erinnere mich nicht wirklich). Fehlerbehandlung mit ASP MVC / Web API und IIS ist ein Schmerz, froh, dass Sie es geschafft haben, es zum Laufen zu bringen;)
Guillaume
0

In meinem speziellen Fall hatte meine Fehlerseite eine Masterseite mit einem Benutzersteuerelement, das versuchte, die Sitzung zu verwenden. Wenn die Sitzung nicht verfügbar ist, wird eine HttpException angezeigt: "Der Sitzungsstatus kann nur verwendet werden, wenn enableSessionState entweder in einer Konfigurationsdatei oder in der Page-Direktive auf true gesetzt ist." Die einfachste Lösung besteht darin, zu statischem HTML zu wechseln. Die zweit einfachste Lösung besteht darin, eine einfachere Fehlerseite zu verwenden. Die schwierigste Lösung besteht darin, unglaublich sicher zu stellen, dass Ihre Fehlerseite nirgendwo Annahmen trifft (so dass die Sitzung beispielsweise keine Ausnahme auslöst) kann unmöglich Fehler aus.

David Eison
quelle
0

Ich habe herausgefunden, dass Sie, wenn Sie redirectMode = "ResponseRewrite" verwenden, etwas im Umschreibbereich der Datei web.config hinzufügen müssen. Problem ist, wenn Ihre Website kaputt ist! Sie können keine URL-Umschreibung vornehmen, da Ihre Site nicht die "virtual.aspx" aufrufen kann, die Ihre Umschreibung übernimmt!

Colin Wiseman
quelle
0

Laut @ Amilas Beitrag und Bestätigung und Vervollständigung dieses Beitrags habe ich das gleiche Problem, ich grabe viel bei Google, hatte aber keine Chance, die richtige Antwort zu finden. Das Problem ist, wenn Sie mit arbeiten ASP.Net Web Application, ob es sich MVCum einen benutzerdefinierten Fehler handelt oder nicht, mit dem Sie auf die alte Art und Weise keinen benutzerdefinierten Fehler erzielen können Webform project.
Hier die Option, wenn Sie verwenden ASP.Net Web Application(ob es eine ist MVCoder nicht):

In meinen Szenarien möchte ich nur einen benutzerdefinierten Fehler für einen bestimmten 404-Fehler definieren. Der andere Fehler ist genauso definiert wie der 404-Fehler:


Senario1: Ihre benutzerdefinierte Seite ist eine einfache HTMLDatei und befindet sich in 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: Ihre benutzerdefinierte Seite ist eine aspxSeite und wird platziert in 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>

Hinweis: Ich entferne die Aspx-Erweiterung aufgrund von RouteConfig.csin ASP.net application. Sie können sie verwenden, ErrorPage.aspxwenn Sie möchten. Sie ist optional.


Senario3: Ihre benutzerdefinierte Seite ist eine aspxSeite und befindet sich in [ex: Page folder in The root (~/Page/ErrorPage.aspx)]:
Der Tipp hier, den ich bemerkt habe, ist , dass Sie die Root-Adressierung NICHT verwenden sollten~/ . Also adressiere ich einfach ohne ~/Markierung:

<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>
Reza Paidar
quelle