ASP.NET Custom 404 zwraca 200 OK zamiast 404 nie znaleziono

80

Po próbie skonfigurowania mojej witryny pod kątem Narzędzi Google dla webmasterów stwierdziłem, że moja niestandardowa strona ASP.NET 404 nie zwraca kodu stanu 404. Wyświetlił właściwą stronę niestandardową i poinformował przeglądarkę, że wszystko jest w porządku. Jest to uważane za pozorny błąd 404 lub fałszywy 404. Google tego nie lubi. Znalazłem więc wiele artykułów na ten temat, ale rozwiązanie, którego szukałem, nie działało.

Rozwiązanie, nad którym chcę pracować, polega na dodaniu następujących dwóch wierszy do kodu znajdującego się za metodą Page_Load niestandardowej strony 404.

Response.Status = "404 Not Found";
Response.StatusCode = 404;

To nie działa. Strona nadal zwraca 200 OK. Zauważyłem jednak, że jeśli na stałe zakoduję następujący kod w kodzie projektowym, będzie on działał poprawnie.

<asp:Content ID="ContentMain" ContentPlaceHolderID="ContentPlaceHolderMaster" runat="server">

<%
    Response.Status = "404 Not Found";
    Response.StatusCode = 404;
%>

 ... Much more code ...

</asp:content>

Strona korzysta ze strony wzorcowej. I konfiguruję niestandardowe strony błędów w moim pliku web.config. Naprawdę wolałbym użyć kodu stojącego za opcją, ale nie wydaje mi się, aby działało bez umieszczenia hackowania kodu wbudowanego w projekcie / układzie.

Bobby Cannon
źródło
Co wskazuje przeglądarka? Używam dodatku Header Spy dla przeglądarki Firefox.
Bobby Cannon,
Odpowiedź szpiega nagłówka: HTTP / 1.1 404 Not Found Data: Sun, 07 Dec 2008 06:21:20 GMT
Ryan Cook
Czy używasz strony wzorcowej? Może o to chodzi. Spróbuję stworzyć stronę bez strony wzorcowej ...
Bobby Cannon,
Nie, nie byłam, ale mogę też szybko sprawdzić, spróbuję z jednym.
Ryan Cook
Tak, to wszystko! Strona wzorcowa spowodowała 200 OK
Ryan Cook

Odpowiedzi:

72

Rozwiązanie:

Okazało się, że problemem było użycie strony wzorcowej. Uruchomiłem go, ustawiając kod stanu później w cyklu życia stron, oczywiście renderowanie strony wzorcowej było resetowane, więc nadpisałem metodę renderowania i ustawiłem ją po zakończeniu renderowania.

protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);
    Response.StatusCode = 404;
}

Można by wykonać więcej pracy, aby dowiedzieć się, kiedy dokładnie strona wzorcowa ustawia status, ale zostawię to Tobie.


Oryginalny post:

Udało mi się sprawić, że testowa aplikacja internetowa działa dobrze, a przynajmniej wyświetliła niestandardową stronę błędu i zwróciła kod stanu 404. Nie mogę powiedzieć, co jest nie tak z twoją aplikacją, ale mogę ci powiedzieć, co zrobiłem:

1) Edytowano plik web.config pod kątem błędów niestandardowych:

<customErrors mode="On">
  <error statusCode="404" redirect="404.aspx"/>
</customErrors>

2) Dodano stronę 404.aspx i ustawiono kod stanu na 404.

public partial class _04 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.StatusCode = 404;
    }
}

O to chodzi, jeśli przejdę do dowolnego rozszerzenia strony, które jest przetwarzane przez Asp.Net i nie istnieje, mój dziennik skrzypka wyraźnie pokazuje 404, oto nagłówek:

HTTP/1.1 404 Not Found
Server: Microsoft-IIS/5.1
Date: Sun, 07 Dec 2008 06:04:13 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 533

Jeśli teraz przejdę do strony, która nie jest przetwarzana przez Asp.Net, na przykład do pliku htm, strona niestandardowa nie jest wyświetlana, a 404 skonfigurowany przez IIS jest wyświetlany.

Oto post, który zawiera więcej szczegółów, które mogą być przydatne dla Ciebie i Twojego problemu, mój test przekierowuje na nową stronę, więc adres URL żądanego pliku jest prawie utracony (z wyjątkiem tego, który znajduje się w ciągu zapytania) .

Niestandardowe strony błędów Google 404 i .NET

Odpowiedź szpiega nagłówka:

HTTP/1.1 404 Not Found
Date: Sun, 07 Dec 2008 06:21:20 GMT
Ryan Cook
źródło
4
Header Spy to dodatek Header Spy dla Firefoksa
Kiquenet
Jak to działa w przypadku statycznych .htmlniestandardowych stron błędów?
ebyrob
28

Miałem podobny problem, chciałem wyświetlić niestandardową stronę jako 404 (czyli ASPX) i działała dobrze na hoście lokalnym, ale gdy tylko zdalny gość połączy się, otrzyma ogólny IIS 404.

Rozwiązaniem było dodanie

Response.TrySkipIisCustomErrors = true;

Przed zmianą Response.StatusCode.

Znalezione przez Rick Strahl http://www.west-wind.com/weblog/posts/745738.aspx

Gary
źródło
12

Rozwiązanie IIS 7 polega po prostu na dodaniu tego do pliku web.config:

<system.webServer>
  <httpErrors existingResponse="Replace">
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" prefixLanguageFilePath="" path="404.htm" responseMode="File" />
    <error statusCode="500" prefixLanguageFilePath="" path="500.htm" responseMode="File" />
  </httpErrors>
</system.webServer>

http://forums.asp.net/t/1563128.aspx/1

Nick D.
źródło
3
Pracował dla mnie! Jedyne rozwiązanie, które zadziałało, prawdopodobnie ze względu na wersję, jak zauważyłeś ... Uwaga: w tym rozwiązaniu można używać tylko statycznych plików .htm.
squarecandy
11

Spróbuj wywołać Response.End (), aby pominąć renderowanie ...

Response.Status = "404 Not Found";
Response.StatusCode = 404;
Response.End();
return;
Jason Goemaat
źródło
9

Po wielu testach i rozwiązywaniu problemów okazuje się, że niektórzy dostawcy hostingu mogą zakłócać kod zwrotny. Udało mi się to obejść, stosując „hack” w treści.

<%
// This code is required for host that do special 404 handling...
Response.Status = "404 Not Found";
Response.StatusCode = 404;
%>

Umożliwi to stronie zwrócenie poprawnego kodu powrotu bez względu na wszystko.

Bobby Cannon
źródło
1

Udało mi się obejść ten problem, używając następującej konfiguracji w formularzach internetowych asp.net przy użyciu platformy .NET 3.5.

Wzorzec, który zaimplementowałem, omija niestandardowe rozwiązanie przekierowania .NET w pliku web.config, ponieważ napisałem własne, aby obsługiwać wszystkie scenariusze z poprawnym kodem stanu HTTP w nagłówku.

Po pierwsze, sekcja customErrors w pliku web.config wygląda następująco:

<customErrors mode="RemoteOnly" defaultRedirect="~/error.htm" />

Ta konfiguracja zapewnia, że ​​tryb CustomErrors jest włączony, ustawienie, którego będziemy potrzebować później, i zapewnia opcję all-else-fail dla defaultRedirect error.htm. Przyda się to, gdy nie mam modułu obsługi określonego błędu lub jest coś w rodzaju zerwanego połączenia z bazą danych.

Po drugie, oto globalne zdarzenie asax Error:

protected void Application_Error(object sender, EventArgs e)
    {
       HandleError();
    }

    private void HandleError()
    {
        var exception = Server.GetLastError();
        if (exception == null) return;

        var baseException = exception.GetBaseException();

        bool errorHandled = _applicationErrorHandler.HandleError(baseException);
        if (!errorHandled) return;


        var lastError = Server.GetLastError();
    if (null != lastError && HttpContext.Current.IsCustomErrorEnabled)
    {
        Elmah.ErrorSignal.FromCurrentContext().Raise(lastError.GetBaseException());
        Server.ClearError();
    }
    }

Ten kod przekazuje odpowiedzialność za obsługę błędu na inną klasę. Jeśli błąd nie jest obsługiwany, a CustomErrors jest włączony, oznacza to, że mamy przypadek, w którym jesteśmy na produkcji i jakoś błąd nie został obsłużony. Wyczyścimy to tutaj, aby użytkownik nie mógł go zobaczyć, ale zaloguj się do Elmah, abyśmy wiedzieli, co się dzieje.

Klasa applicationErrorHandler wygląda następująco:

public bool HandleError(Exception exception)
        {
            if (exception == null) return false;

            var baseException = exception.GetBaseException();

            Elmah.ErrorSignal.FromCurrentContext().Raise(baseException);

            if (!HttpContext.Current.IsCustomErrorEnabled) return false;

            try
            {

                var behavior = _responseBehaviorFactory.GetBehavior(exception);
                if (behavior != null)
                {
                    behavior.ExecuteRedirect();
                    return true;
                }
            }
            catch (Exception ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
            }
            return false;
        }

Ta klasa zasadniczo używa wzorca polecenia, aby zlokalizować odpowiednią procedurę obsługi błędów dla typu zgłoszonego błędu. Ważne jest, aby używać Exception.GetBaseException () na tym poziomie, ponieważ prawie każdy błąd zostanie opakowany w wyjątek wyższego poziomu. Na przykład wykonanie „rzutu nowego System.Exception ()” z dowolnej strony aspx spowoduje odebranie wyjątku HttpUnhandledException na tym poziomie, a nie System.Exception.

Kod „fabryczny” jest prosty i wygląda następująco:

public ResponseBehaviorFactory()
    {
        _behaviors = new Dictionary<Type, Func<IResponseBehavior>>
                        {
                            {typeof(StoreException), () => new Found302StoreResponseBehavior()},
                            {typeof(HttpUnhandledException), () => new HttpExceptionResponseBehavior()},
                            {typeof(HttpException), () => new HttpExceptionResponseBehavior()},
                            {typeof(Exception), () => new Found302DefaultResponseBehavior()}
                        };
    }

    public IResponseBehavior GetBehavior(Exception exception)
    {                                                                               
        if (exception == null) throw new ArgumentNullException("exception");

        Func<IResponseBehavior> behavior;
        bool tryGetValue = _behaviors.TryGetValue(exception.GetType(), out behavior);

        //default value here:
        if (!tryGetValue)
            _behaviors.TryGetValue(typeof(Exception), out behavior);

        if (behavior == null)
            Elmah.ErrorSignal.FromCurrentContext().Raise(
                new Exception(
                    "Danger! No Behavior defined for this Exception, therefore the user might have received a yellow screen of death!",
                    exception));
        return behavior();
    }

W końcu mam rozszerzalny schemat obsługi błędów. W każdym ze zdefiniowanych „zachowań” mam niestandardową implementację dla typu błędu. Na przykład wyjątek HTTP zostanie sprawdzony pod kątem kodu stanu i odpowiednio obsłużony. Kod stanu 404 będzie wymagał Server.Transfer zamiast Request.Redirect, wraz z odpowiednim kodem stanu zapisanym w nagłówku.

Mam nadzieję że to pomoże.

letgetsilly
źródło
0

Możesz użyć poniższego kodu:

 Response.TrySkipIisCustomErrors = True
 Response.Status = "404 Not Found"
 Response.AddHeader("Location", "{your-path-to-your-404-page}")
Rami Zebian
źródło