web-dev-qa-db-fra.com

ASP.NET 404 personnalisé retournant 200 OK au lieu de 404 introuvable

Après avoir essayé de configurer mon site pour les outils Google pour les webmasters, j'ai constaté que ma page ASP.NET 404 personnalisée ne renvoyait pas le code d'état 404. Il a affiché la page personnalisée correcte et a dit au navigateur que tout allait bien. Il s'agit d'un 404 souple ou d'un faux 404. Google n'aime pas cela. J'ai donc trouvé de nombreux articles sur la question, mais la solution que je souhaite ne semble pas fonctionner.

La solution que je veux travailler consiste à ajouter les deux lignes suivantes au code derrière la méthode▶Load de la page 404 personnalisée.

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

Ça ne marche pas. La page renvoie toujours 200 OK. J'ai cependant constaté que si je codais en dur le code suivant dans le code de conception, cela fonctionnerait correctement.

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

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

 ... Much more code ...

</asp:content>

La page utilise une page maître. Et je configure des pages d'erreur personnalisées dans mon web.config. Je préférerais vraiment utiliser le code derrière l'option mais je n'arrive pas à le faire fonctionner sans mettre un code en ligne hack dans la conception/mise en page.

77
Bobby Cannon

Solution:

Il s'est avéré que le problème était l'utilisation de la page maître. Je l'ai fait fonctionner en définissant le code d'état plus tard dans le cycle de vie des pages, évidemment le rendu de la page maître la réinitialisait, donc j'ai outrepassé la méthode de rendu et je l'ai définie une fois le rendu terminé.

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

Plus de travail pourrait être fait pour savoir exactement quand la page maître définit le statut, mais je vous laisse cela.


Message d'origine:

J'ai réussi à faire fonctionner une application Web de test, elle a au moins affiché la page d'erreur personnalisée et renvoyé un code d'état 404. Je ne peux pas vous dire ce qui ne va pas avec votre application, mais je peux vous dire ce que j'ai fait:

1) Modification du web.config pour les erreurs personnalisées:

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

2) Ajout d'une page 404.aspx et définissez le code d'état sur 404.

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

C'est à ce sujet, si je vais sur n'importe quelle extension de page qui est traitée par Asp.Net et n'existe pas, mon journal de violoneux montre clairement un 404, voici l'en-tête:

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

Maintenant, si je vais sur une page qui n'est pas traitée par Asp.Net, comme un fichier htm, la page personnalisée ne s'affiche pas et le 404 configuré par IIS s'affiche.

Voici un article qui rentre dans plus de détails qui peuvent vous être utiles et votre problème, mon test fait une redirection vers la nouvelle page donc l'url du fichier demandé est à peu près perdue (sauf sa dans la chaîne de requête) .

pages d'erreur personnalisées Google 404 et .NET

Réponse d'espion d'en-tête:

HTTP/1.1 404 Not Found
Date: Sun, 07 Dec 2008 06:21:20 GMT
71
Ryan Cook

J'ai eu un problème similaire, je veux afficher une page personnalisée sous la forme d'un 404 (qui est ASPX) et cela a bien fonctionné sur localhost mais dès qu'un visiteur distant se connecte, il obtient le générique IIS 404 .

La solution à cela était d’ajouter

Response.TrySkipIisCustomErrors = true;

Avant de modifier le Response.StatusCode.

Trouvé via Rick Strahl http://www.west-wind.com/weblog/posts/745738.aspx

27
gary

La solution IIS 7 consiste simplement à ajouter ceci à votre fichier 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

12
Nick D

Essayez d'appeler Response.End () pour ignorer le rendu ...

Response.Status = "404 Not Found";
Response.StatusCode = 404;
Response.End();
return;
10
Jason Goemaat

Après de nombreux tests et dépannage, il apparaît que certains hébergeurs peuvent interférer avec le code retour. J'ai pu contourner cela en appliquant un "hack" dans le contenu.

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

Cela permettra à la page de renvoyer le bon code de retour quoi qu'il arrive.

7
Bobby Cannon

J'ai pu contourner ce problème en utilisant la configuration suivante dans les formulaires Web asp.net à l'aide de .NET 3.5.

Le modèle que j'ai implémenté contourne la solution de redirection personnalisée de .NET dans le web.config car j'ai écrit le mien pour gérer tous les scénarios avec le code d'état HTTP correct dans l'en-tête.

Tout d'abord, la section customErrors du web.config ressemble à ceci:

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

Cette configuration garantit que le mode CustomErrors est activé, un paramètre dont nous aurons besoin plus tard, et fournit une option tout autre échec pour le defaultRedirect de error.htm. Cela sera utile lorsque je n'ai pas de gestionnaire pour l'erreur spécifique, ou s'il y a quelque chose dans le sens d'une connexion de base de données rompue.

Deuxièmement, voici l'événement d'erreur asax global:

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

Ce code délègue la responsabilité de gérer l'erreur à une autre classe. Si l'erreur n'est pas gérée et que CustomErrors est activé, cela signifie que nous avons un cas où nous sommes en production et d'une manière ou d'une autre une erreur n'a pas été gérée. Nous allons l'effacer ici afin d'empêcher l'utilisateur de le voir, mais connectez-le dans Elmah afin que nous sachions ce qui se passe.

La classe applicationErrorHandler ressemble à ceci:

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

Cette classe utilise essentiellement le modèle de commande pour localiser le gestionnaire d'erreurs approprié pour le type d'erreur émis. Il est important d'utiliser Exception.GetBaseException () à ce niveau, car presque toutes les erreurs seront enveloppées dans une exception de niveau supérieur. Par exemple, l'exécution de "throw new System.Exception ()" à partir de n'importe quelle page aspx entraînera la réception d'une HttpUnhandledException à ce niveau, et non d'une System.Exception.

Le code "usine" est simple et ressemble à ceci:

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

En fin de compte, j'ai une configuration de schéma de gestion des erreurs extensible. Dans chacun des "comportements" définis, j'ai une implémentation personnalisée pour le type d'erreur. Par exemple, une exception Http sera inspectée pour le code d'état et gérée de manière appropriée. Un code d'état 404 nécessitera un Server.Transfer au lieu d'un Request.Redirect, ainsi que le code d'état approprié écrit dans l'en-tête.

J'espère que cela t'aides.

1
letsgetsilly