Bir ASP.NET MVC eyleminden HTTP 404 yanıtı göndermenin uygun yolu nedir?


92

Rota verilirse:

{FeedName} / {ItemPermalink}

örn: / Blog / Merhaba Dünya

Öğe yoksa, bir 404 döndürmek istiyorum. Bunu ASP.NET MVC'de yapmanın doğru yolu nedir?


Bu soruyu sorduğunuz için teşekkürler btw. Bu benim standart proje eklemelerime gidiyor: D
Erik van Brakel

Yanıtlar:


69

Kalçadan çekim (kovboy kodlaması ;-)), şöyle bir şey öneririm:

Denetleyici:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return new HttpNotFoundResult("This doesn't exist");
    }
}

HttpNotFoundResult:

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

namespace YourNamespaceHere
{
    /// <summary>An implementation of <see cref="ActionResult" /> that throws an <see cref="HttpException" />.</summary>
    public class HttpNotFoundResult : ActionResult
    {
        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with the specified <paramref name="message"/>.</summary>
        /// <param name="message"></param>
        public HttpNotFoundResult(String message)
        {
            this.Message = message;
        }

        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with an empty message.</summary>
        public HttpNotFoundResult()
            : this(String.Empty) { }

        /// <summary>Gets or sets the message that will be passed to the thrown <see cref="HttpException" />.</summary>
        public String Message { get; set; }

        /// <summary>Overrides the base <see cref="ActionResult.ExecuteResult" /> functionality to throw an <see cref="HttpException" />.</summary>
        public override void ExecuteResult(ControllerContext context)
        {
            throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message);
        }
    }
}
// By Erik van Brakel, with edits from Daniel Schaffer :)

Bu yaklaşımı kullanarak çerçeve standartlarına uyarsınız. Orada zaten bir HttpUnauthorizedResult var, bu nedenle bu, daha sonra kodunuzu koruyan başka bir geliştiricinin gözünde çerçeveyi genişletecektir (bilirsiniz, nerede yaşadığınızı bilen psikopat).

HttpUnauthorizedResult'un nasıl elde edildiğini görmek için montaja bir göz atmak için reflektörü kullanabilirsiniz, çünkü bu yaklaşımın herhangi bir şeyi gözden kaçırıp kaçırmadığını bilmiyorum (neredeyse çok basit görünüyor).


Şu anda HttpUnauthorizedResult'a bir göz atmak için reflektör kullandım. Görünüşe göre 0x191 (401) yanıtında Durum Kodunu ayarlıyorlar. Bu 401 için işe yarasa da, yeni değer olarak 404'ü kullanarak Firefox'ta sadece boş bir sayfa görüyorum. Internet Explorer, varsayılan bir 404'ü gösterir (ASP.NET sürümünü değil). Web geliştiricisi araç çubuğunu kullanarak FF'deki 404 Not Found yanıtı gösteren başlıkları inceledim. FF'de yanlış yapılandırdığım bir şey olabilir.


Bununla birlikte, Jeff'in yaklaşımının KISS'in güzel bir örneği olduğunu düşünüyorum. Bu örnekteki ayrıntıya gerçekten ihtiyacınız yoksa, yöntemi de iyi çalışıyor.


Evet, Enum'u da fark ettim. Dediğim gibi, bu sadece kaba bir örnek, onu geliştirmekten çekinmeyin. Bunun bir bilgi bankası olması gerekiyordu ;-)
Erik van Brakel

Sanırım biraz denize düştüm ... keyfini çıkarın: D
Daniel Schaffer

FWIW, Jeff'in örneği ayrıca özel bir 404 sayfanızın olmasını gerektirir.
Daniel Schaffer

2
HttpContext.Response.StatusCode = 404'ü ayarlamak yerine HttpException atmanın bir sorunu, OnException Denetleyici işleyicisini kullanırsanız (benim yaptığım gibi), HttpExceptions'ı da yakalayacaktır. Bu yüzden sadece Durum Kodunu ayarlamanın daha iyi bir yaklaşım olduğunu düşünüyorum.
Igor Brejc

4
MVC3'te HttpException veya HttpNotFoundResult birçok yönden yararlıdır. @Igor Brejc durumunda, bulunamadı hatasını filtrelemek için OnException'da if ifadesini kullanın .
CallMeLaNN

46

Biz böyle yapıyoruz; bu kod şurada bulunurBaseController

/// <summary>
/// returns our standard page not found view
/// </summary>
protected ViewResult PageNotFound()
{
    Response.StatusCode = 404;
    return View("PageNotFound");
}

öyle aradı

public ActionResult ShowUserDetails(int? id)
{        
    // make sure we have a valid ID
    if (!id.HasValue) return PageNotFound();

bu eylem daha sonra varsayılan bir rotaya bağlanır mı? Nasıl idam edileceğini göremiyorum.
Christian Dalager

2
Şu şekilde çalıştırılabilir: korumalı geçersiz kılma void HandleUnknownAction (string actionName) {PageNotFound (). ExecuteResult (this.ControllerContext); }
Tristan Warner-Smith

Eskiden bu şekilde yapardım, ancak sonucu ve görüntülenen görünümü bölmenin daha iyi bir yaklaşım olduğunu gördüm. Aşağıdaki cevabıma bakın.
Brian Vallelunga

19
throw new HttpException(404, "Are you sure you're in the right place?");

Bunu beğendim çünkü içinde ayarlanan özel hata sayfalarını takip ediyor web.config.
Mike Cole

7

HttpNotFoundResult, kullandığım şey için harika bir ilk adımdır. Bir HttpNotFoundResult döndürmek iyidir. O zaman soru şu, sırada ne var?

HandleNotFoundAttribute adında bir 404 hata sayfası gösteren bir eylem filtresi oluşturdum. Bir görünüm döndürdüğü için, denetleyici başına özel bir 404 görünümü oluşturabilir veya varsayılan bir paylaşılan 404 görünümü kullanabilirsiniz. Bu, bir denetleyicide belirtilen eylem mevcut olmadığında bile çağrılır, çünkü çerçeve 404 durum kodlu bir HttpException oluşturur.

public class HandleNotFoundAttribute : ActionFilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        var httpException = filterContext.Exception.GetBaseException() as HttpException;
        if (httpException != null && httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
        {
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; // Prevents IIS from intercepting the error and displaying its own content.
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound;
            filterContext.Result = new ViewResult
                                        {
                                            ViewName = "404",
                                            ViewData = filterContext.Controller.ViewData,
                                            TempData = filterContext.Controller.TempData
                                        };
        }
    }
}


6

Kullanılması ActionFilter olduğunu korumak için sert bir hatayla filtre ihtiyacını atmak her özelliğinde ayarlanan olması sebebiyledir. Ya ayarlamayı unutursak? Bir yol, OnExceptiontemel denetleyicide türetmektir . Bir BaseControllertüretilmiş tanımlamanız gerekir Controllerve tüm denetleyicilerinizin türetmesi gerekir BaseController. Bir temel denetleyiciye sahip olmak en iyi uygulamadır.

ExceptionYanıt durum kodunu kullanmanın 500 olup olmadığına dikkat edin, bu nedenle, Bulunamadı için 404 ve Yetkisiz için 401 olarak değiştirmemiz gerekir. Yukarıda bahsettiğim gibi, filtre özelliğini kullanmaktan kaçınmak için OnExceptiongeçersiz kılmaları kullanın BaseController.

Yeni MVC 3, tarayıcıya boş bir görünüm döndürerek daha da zahmetli hale getiriyor. Biraz araştırmadan sonra en iyi çözüm buradaki cevabıma dayanmaktadır ASP.Net MVC 3'te HttpNotFound () için bir görünüm nasıl döndürülür?

Daha fazla rahatlık sağlamak için buraya yapıştırıyorum:


Biraz çalıştıktan sonra. İçin geçici çözüm MVC 3 Burada tüm türetmek olduğunu HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResultsınıfları ve uygulamak yeni (geçersiz kılma) HttpNotFoundiçinde () yöntemini BaseController.

Tüm türetilmiş Denetleyiciler üzerinde 'kontrol' sahibi olmanız için temel Denetleyiciyi kullanmak en iyi uygulamadır.

Ben yeni oluşturmak HttpStatusCodeResultdeğil türediği, sınıf ActionResultancak gelen ViewResultgörünüm veya herhangi işlemek Viewbelirterek istediğiniz ViewNameözelliği. Aslini izleyin HttpStatusCodeResultayarlamak HttpContext.Response.StatusCodeve HttpContext.Response.StatusDescriptionama sonra base.ExecuteResult(context)tekrar ben kendisinden elde edildiğinden uygun görünümü oluşturur ViewResult. Yeterince basit mi? Umarım bu MVC çekirdeğinde uygulanır.

Aşağıya bakın BaseController:

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

Eyleminizde böyle kullanmak için:

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

Ve _Layout.cshtml'de (ana sayfa gibi)

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

Ek olarak, kodda yorumladığım gibi özel bir görünüm kullanabilir Error.shtmlveya yeni NotFound.cshtmlbir görünüm oluşturabilir ve durum açıklaması ve diğer açıklamalar için bir görünüm modeli tanımlayabilirsiniz.


Her zaman bir temel denetleyiciyi yenen global bir filtre kaydedebilirsiniz çünkü ana denetleyicinizi kullanmak için UNUTMAYINIZ!
John Culviner

:) Bunun MVC4'te hala bir sorun olduğundan emin değilim. O zaman demek istediğim, başka biri tarafından cevaplanan HandleNotFoundAttribute filtresidir. Her işlem için uygulanmasına gerek yoktur. Örneğin, yalnızca id parametresi olan ancak Index () eylemi olmayan eylemler için uygundur. HandleNotFoundAttribute için değil, özel bir HandleErrorAttribute için genel filtre üzerinde anlaştım.
CallMeLaNN

MVC3'te de olduğunu sanıyordum, emin değilim.
Yanıtla
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.