41 votes

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

Est-il un moyen de retourner le même point de vue à chaque fois qu'un HttpNotFoundResult est retourné à partir d'un contrôleur? Comment avez-vous préciser ce point de vue? Je devine que la configuration d'une page d'erreur 404 dans le site web.config pourrait fonctionner, mais je voulais savoir si il y avait une meilleure façon de gérer ce résultat.

Modifier / Suivi:

J'ai fini par utiliser la solution se trouve dans la deuxième réponse à cette question avec quelques légères améliorations pour ASP.Net MVC 3 pour gérer mon erreur 404: Comment puis-je gérer correctement une erreur 404 dans ASP.Net MVC?

65voto

Darin Dimitrov Points 528142

HttpNotFoundResult ne rend pas une vue. Il définit simplement le code d'état à 404 et renvoie un résultat vide, ce qui est utile pour des tâches 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 la vue configurée dans web.config:

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

17voto

Germán Points 867

Cette solution combine IResultFilter et IExceptionFilter pour attraper soit jeté HttpException ou retourné HttpStatusCodeResult dans 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 de sorte que, en spécifiant le code de statut http de la HttpException ou le béton HttpStatusCodeResult pour lesquelles vous wan't pour 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 jetés ou retourné à l'intérieur d'une action. Il ne gère pas les erreurs qui se produisent avant MVC sélectionne une action appropriée et contrôleur comme cette commune problemns:

  • Inconnu routes
  • Inconnu contrôleurs
  • Inconnu actions

Pour le traitement de ces types de NotFound erreurs, de combiner cette solution avec d'autres solutions à trouver dans stackoverflow.

15voto

CallMeLaNN Points 1945

Infos utiles à partir de @Darin Dimitrov qu' HttpNotFoundResult est en fait le retour de résultats vide.

Après une étude. La solution de contournement pour MVC 3 , ici, est de tirer tous HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult classes et de mettre en œuvre de nouvelles (remplaçant) HttpNotFound() la méthode en BaseController.

Il est préférable de s'exercer à l'utilisation de la base de Contrôleur de sorte que vous avez un "contrôle" sur tous les dérivés des Contrôleurs.

- Je créer un nouveau HttpStatusCodeResult classe, de ne pas dériver à partir d' ActionResult , mais à partir de ViewResult pour le rendu de la vue ou de tout View vous voulez en spécifiant l' ViewName de la propriété. J'ai suivi l'originale HttpStatusCodeResult pour définir l' HttpContext.Response.StatusCode et HttpContext.Response.StatusDescription mais ensuite, base.ExecuteResult(context) rendra la vue, car encore une fois je dérivent ViewResult. Assez Simple, est-il? J'espère que ce sera mis en œuvre dans le MVC de base.

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

À utiliser dans votre action comme ceci:

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

Et dans _Layout.cshtml (comme la page principale)

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

En outre, vous pouvez utiliser un affichage personnalisé comme Error.shtml ou créer de nouveaux NotFound.cshtml comme je l'ai commenté dans le code et vous pouvez définir un modèle de vue pour le statut de la description et d'autres explications.

2voto

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X