web-dev-qa-db-fra.com

Comment retourner une vue pour HttpNotFound () dans ASP.Net MVC 3?

Existe-t-il un moyen de retourner la même vue chaque fois qu'un HttpNotFoundResult est renvoyé par un contrôleur? Comment spécifiez-vous cette vue? Je suppose que la configuration d'une page 404 dans le fichier web.config pourrait fonctionner, mais je voulais savoir s'il existait un meilleur moyen de gérer ce résultat.

Editer/Suivre:

J'ai fini par utiliser la solution trouvée dans la deuxième réponse à cette question avec quelques légères modifications pour ASP.Net MVC 3 afin de gérer mes 404: Comment gérer correctement les 404 dans ASP.Net MVC?

39
Dave Brace

HttpNotFoundResult ne rend pas une vue. Il définit simplement le code d'état sur 404 et renvoie un résultat vide, ce qui est utile pour des choses comme AJAX, mais si vous souhaitez une page d'erreur 404 personnalisée, vous pouvez throw new HttpException(404, "Not found"), ce qui rendra automatiquement l'affichage configuré dans web.config:

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
   <error statusCode="404" redirect="/Http404.html" />
</customErrors>
64
Darin Dimitrov

Cette solution combine IResultFilter et IExceptionFilter pour intercepter une exception HttpException ou un HttpStatusCodeResult renvoyé depuis une action.

public class CustomViewForHttpStatusResultFilter: IResultFilter, IExceptionFilter
{
    string viewName;
    int statusCode;

    public CustomViewForHttpStatusResultFilter(HttpStatusCodeResult prototype, string viewName)
        : this(prototype.StatusCode, viewName) {
    }

    public CustomViewForHttpStatusResultFilter(int statusCode, string viewName) {
        this.viewName = viewName;
        this.statusCode = statusCode;
    }

    public void OnResultExecuted(ResultExecutedContext filterContext) {
        HttpStatusCodeResult httpStatusCodeResult = filterContext.Result as HttpStatusCodeResult;

        if (httpStatusCodeResult != null && httpStatusCodeResult.StatusCode == statusCode) {
            ExecuteCustomViewResult(filterContext.Controller.ControllerContext);

        }
    }

    public void OnResultExecuting(ResultExecutingContext filterContext) {
    }

    public void OnException(ExceptionContext filterContext) {
        HttpException httpException = filterContext.Exception as HttpException;

        if (httpException != null && httpException.GetHttpCode() == statusCode) {
            ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
            // This causes ELMAH not to log exceptions, so commented out
            //filterContext.ExceptionHandled = true;
        }
    }

    void ExecuteCustomViewResult(ControllerContext controllerContext) {
        ViewResult viewResult = new ViewResult();
        viewResult.ViewName = viewName;
        viewResult.ViewData = controllerContext.Controller.ViewData;
        viewResult.TempData = controllerContext.Controller.TempData;
        viewResult.ExecuteResult(controllerContext);
        controllerContext.HttpContext.Response.TrySkipIisCustomErrors = true;            
    }
}

Vous pouvez enregistrer ce filtre en spécifiant le code de statut http de l'exception HttpException ou le code concret HttpStatusCodeResult pour lequel vous souhaitez afficher la vue personnalisée.

GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(new HttpNotFoundResult(), "Error404"));
// alternate syntax
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(404, "Error404"));

Il gère les exceptions et HttpStatusCodeResult levés ou retournés au sein d'une action. Il ne gérera pas les erreurs qui se produiront avant que MVC ne sélectionne une action appropriée et un contrôleur comme celui-ci:

  • Itinéraires inconnus
  • Contrôleurs inconnus
  • Actions inconnues

Pour gérer ces types d’erreurs NotFound, combinez cette solution avec d’autres solutions proposées dans stackoverflow.

17
Germán

Informations utiles de @Darin Dimitrov sur le fait que HttpNotFoundResult renvoie un résultat vide.

Après quelques études. La solution de contournement pour MVC 3 ici consiste à dériver toutes les classes HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult et à mettre en œuvre la méthode new (en le remplaçant) HttpNotFound () dans BaseController.

Il est recommandé d’utiliser le contrôleur de base pour pouvoir contrôler tous les contrôleurs dérivés.

Je crée une nouvelle classe HttpStatusCodeResult, non pas à partir de ActionResult mais à partir de ViewResult pour rendre la vue ou toute View souhaitée en spécifiant la propriété ViewName. Je suis la HttpStatusCodeResult d'origine pour définir le HttpContext.Response.StatusCode et le HttpContext.Response.StatusDescription, mais base.ExecuteResult(context) rendra la vue appropriée, car là encore, je dérive de ViewResult. C'est assez simple? J'espère que cela sera implémenté dans le noyau MVC.

Voir mon BaseController ci-dessous:

using System.Web;
using System.Web.Mvc;

namespace YourNamespace.Controllers
{
    public class BaseController : Controller
    {
        public BaseController()
        {
            ViewBag.MetaDescription = Settings.metaDescription;
            ViewBag.MetaKeywords = Settings.metaKeywords;
        }

        protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
        {
            return new HttpNotFoundResult(statusDescription);
        }

        protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
        {
            return new HttpUnauthorizedResult(statusDescription);
        }

        protected class HttpNotFoundResult : HttpStatusCodeResult
        {
            public HttpNotFoundResult() : this(null) { }

            public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }

        }

        protected class HttpUnauthorizedResult : HttpStatusCodeResult
        {
            public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
        }

        protected class HttpStatusCodeResult : ViewResult
        {
            public int StatusCode { get; private set; }
            public string StatusDescription { get; private set; }

            public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }

            public HttpStatusCodeResult(int statusCode, string statusDescription)
            {
                this.StatusCode = statusCode;
                this.StatusDescription = statusDescription;
            }

            public override void ExecuteResult(ControllerContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                context.HttpContext.Response.StatusCode = this.StatusCode;
                if (this.StatusDescription != null)
                {
                    context.HttpContext.Response.StatusDescription = this.StatusDescription;
                }
                // 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
                // 2. Uncomment this and change to any custom view and set the name here or simply
                // 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
                //this.ViewName = "Error";
                this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
                base.ExecuteResult(context);
            }
        }
    }
}

Pour utiliser dans votre action comme ceci:

public ActionResult Index()
{
    // Some processing
    if (...)
        return HttpNotFound();
    // Other processing
}

Et dans _Layout.cshtml (comme un gabarit)

<div class="content">
    @if (ViewBag.Message != null)
    {
        <div class="inlineMsg"><p>@ViewBag.Message</p></div>
    }
    @RenderBody()
</div>

De plus, vous pouvez utiliser une vue personnalisée telle que Error.shtml ou créer un nouveau NotFound.cshtml comme je l'ai commenté dans le code et vous pouvez définir un modèle de vue pour la description du statut et d'autres explications.

14
CallMeLaNN
protected override void HandleUnknownAction(string actionName)
{
    ViewBag.actionName  = actionName;
    View("Unknown").ExecuteResult(this.ControllerContext);
}
2
Dure_Sameen

Voici la vraie réponse qui permet de personnaliser entièrement la page d’erreur en un seul endroit.

Ajoutez ce code au contrôleur:

        if (bad) {
            Response.Clear();
            Response.TrySkipIisCustomErrors = true;
            Response.Write(product + I(" Toodet pole"));
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            //Response.ContentType = "text/html; charset=utf-8";
            Response.End();
            return null;
        }

Basé sur http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages

0
Andrus

Veuillez suivre ceci si vous voulez une erreur httpnotfound dans votre contrôleur

  public ActionResult Contact()
    {

        return HttpNotFound();
    }
0
user5093161