ASP.NET MVC 404 düzgün işlemek nasıl?


432

RC2 kullanıyorum

URL Yönlendirmesini Kullanma:

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

Yukarıdaki gibi (ilk MVC projesi tarafından varsayılan yol tabloları kurulumu varsayar):

Denetleyicinin kendisinde geçersiz kılınan HandleUnknownAction ():

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

Ancak, önceki stratejiler Kötü / Bilinmeyen bir denetleyiciye yönelik bir isteği işlemez. Örneğin, bir "/ IDoNotExist" yok, bunu talep edersem routing + geçersiz kılmayı kullanırsam 404 değil, web sunucusundan genel 404 sayfasını alıyorum.

Son olarak, sorum şu: MVC çerçevesinin kendisinde bir rota veya başka bir şey kullanarak bu tür bir istek yakalamanın bir yolu var mı?

YA DA sadece 404 işleyicim olarak Web.Config customErrors kullanmak varsayılan ve tüm bunları unutmak gerekir? CustomErrors ile gidersem genel 404 sayfasını / Views dışında doğrudan erişimde Web.Config kısıtlamaları nedeniyle saklamam gerekeceğini varsayarım.


3
404 hatası, sadece bu konuda rahatsız olmaz. 404 görüntülemesine izin verin. veya taşınan bir şeyse, başvurunuz bu isteği almalı ve kalıcı olarak yönlendirme yapmalıdır. 404 uygulama değil, web sunucusuna aittir. iis sayfalarını her zaman hata için özelleştirebilirsiniz.
mamu



4
4 kararlı sürümün daha sonra ve 5 yıldan daha uzun sürmesi, utanç verici.
joelmdev

Yanıtlar:


271

Kod http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx adresinden alınır ve çalışır ASP.net MVC 1.0'da da

Http istisnalarını şu şekilde işleyebilirim:

protected void Application_Error(object sender, EventArgs e)
{
   Exception exception = Server.GetLastError();
   // Log the exception.

   ILogger logger = Container.Resolve<ILogger>();
   logger.Error(exception);

   Response.Clear();

   HttpException httpException = exception as HttpException;

   RouteData routeData = new RouteData();
   routeData.Values.Add("controller", "Error");

   if (httpException == null)
   {
       routeData.Values.Add("action", "Index");
   }
   else //It's an Http Exception, Let's handle it.
   {
       switch (httpException.GetHttpCode())
       {
          case 404:
              // Page not found.
              routeData.Values.Add("action", "HttpError404");
              break;
          case 500:
              // Server error.
              routeData.Values.Add("action", "HttpError500");
              break;

           // Here you can handle Views to other error codes.
           // I choose a General error template  
           default:
              routeData.Values.Add("action", "General");
              break;
      }
  }           

  // Pass exception details to the target error View.
  routeData.Values.Add("error", exception);

  // Clear the error on server.
  Server.ClearError();

  // Avoid IIS7 getting in the middle
  Response.TrySkipIisCustomErrors = true; 

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData));
}

23
güncelleme: http 404 için kontrol kesinlikle gereklidir, ancak 500 aldığınızda hala tam olarak emin değilim. şu anda bu kodun yaptığı bir 200 durum kodu döndürüyorsanız
Simon_Weaver 11:09

6
@Simon_Weaver: kabul etti! Bunun 404 durum kodu döndürmesi gerekir. Aslında 404'lük bir çözelti olarak çözülünceye kadar. Bunu kontrol edin: codinghorror.com/blog/2007/03/…
Matt Kocaj

1
Tüm bu önerinin altında yatan bir kusur var - yürütme Global.asax'a ulaştığında, HttpContext'in çok fazla kısmı eksik. Örnekte belirtildiği gibi denetleyicilerinize geri dönemezsiniz. Üstteki blog bağlantısındaki yorumlara bakın.
Matt Kocaj

3
Yukarıdaki yorumlardan bazıları ve bağlantılı yazıdan bahsedildiği gibi, bu işe yaramaz gibi görünüyor. Hata denetleyicisi vurulur, ancak boş bir ekran geri döner. (mvc 3 kullanarak)
RyanW

4
bir şey doğru gelmiyor, MVC'nin tüm amacı tüm bu soyutlamaları kaldırmak ve yine burada ...
Alex Nolasco

255

404 için gereksinimler

Aşağıdakiler bir 404 çözümü için gereksinimlerim ve aşağıda nasıl uyguladığımı gösteriyor:

  • Eşleşen rotaları kötü eylemlerle ele almak istiyorum
  • Eşleşmeyen rotaları kötü denetleyicilerle ele almak istiyorum
  • Eşleşmemiş rotaları işlemek istiyorum (uygulamamın anlayamadığı rastgele URL'ler) - bu Global.asax veya IIS'ye kadar bu kabarmayı istemiyorum çünkü MVC uygulamama düzgün bir şekilde geri yönlendiremiyorum
  • Yukarıdaki gibi aynı şekilde işlemek için bir yol istiyorum, özel 404s - var olmayan bir nesne için bir kimlik gönderildiği gibi (belki silinmiş)
  • Ben daha sonra (gerekirse daha fazla veri pompalamak hangi bir MVC görünümü (değil statik bir sayfa) dönmek için bütün 404'lerin istiyorum iyi 404 tasarımlar ) ve onlar gerekir HTTP 404 durum kodu döndürecek

Çözüm

Application_Errorİşlenmeyen istisnalar ve günlüğe kaydetme ( Shay Jacoby'nin yanıtı gibi) gibi daha yüksek şeyler için Global.asax'a kaydetmeniz gerektiğini düşünüyorum , ancak 404 işleme değil. Bu yüzden önerim 404 öğeyi Global.asax dosyasından uzak tutuyor.

Adım 1: 404 hata mantığı için ortak bir yere sahip olun

Bu, sürdürülebilirlik için iyi bir fikirdir. İyi tasarlanmış 404 sayfanızdaki gelecekteki geliştirmelerin kolayca uyarlanabilmesi için bir ErrorController kullanın . Ayrıca, yanıtınızın 404 koduna sahip olduğundan emin olun !

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // If the url is relative ('NotFound' route) then replace with Requested path
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Dont get the user stuck in a 'retry loop' by
        // allowing the Referrer to be the same as the Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: insert ILogger here

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

Adım 2: Özel 404 eyleminizi kolayca başlatabileceğiniz ve kabloyu kurabileceğiniz bir temel Controller sınıfı kullanın HandleUnknownAction

ASP.NET MVC'deki 404'lerin çeşitli yerlerde yakalanması gerekir. Birincisi HandleUnknownAction.

InvokeHttp404Yöntem için yeniden yönlendirme için ortak bir yer oluşturur ErrorControllerve yeni Http404eylem. KURU düşün !

public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // If controller is ErrorController dont 'nest' exceptions
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

Adım 3: Denetleyici Fabrikanızda Bağımlılık Enjeksiyonu kullanın ve 404 HttpExceptions'ı bağlayın

Bunun gibi (StructureMap olması gerekmez):

MVC1.0 örneği:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

MVC2.0 örneği:

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == 404)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }

Ben kaynaklarına daha yakın hataları yakalamak daha iyi olduğunu düşünüyorum. Bu yüzden yukarıdaki Application_Errorişleyiciye tercih ederim .

Burası 404'leri yakalamak için ikinci sırada.

4. Adım: Uygulamanıza ayrıştırılamayan URL'ler için Global.asax'a bir NotFound yolu ekleyin

Bu rota Http404eylemimize işaret etmelidir . urlYönlendirme motoru buradaki etki alanı bölümünü sıyırdığı için parametrenin göreli bir URL olacağına dikkat edin ? Bu yüzden 1. Adımda tüm bu koşullu URL mantığına sahibiz.

        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

Bu kendinizi çağırmak değil bir MVC app 404s yakalamak için üçüncü ve son yerdir. Eşleşmeyen rotaları burada yakalamazsanız, MVC sorunu ASP.NET'e (Global.asax) kadar geçirir ve bu durumda bunu gerçekten istemezsiniz.

5. Adım: Son olarak, uygulamanız bir şey bulamadığında 404'leri çağırın

Krediler denetleyicime kötü bir kimlik gönderildiğinde olduğu gibi (kaynak:) MyController:

    //
    // GET: /Detail/ID

    public ActionResult Detail(int ID)
    {
        Loan loan = this._svc.GetLoans().WithID(ID);
        if (loan == null)
            return this.InvokeHttp404(HttpContext);
        else
            return View(loan);
    }

Tüm bu daha az kod ile daha az yerde bağlanabilir eğer güzel olurdu ama bu çözüm daha sürdürülebilir, daha test edilebilir ve oldukça pragmatik olduğunu düşünüyorum.

Şimdiye kadarki geri dönüşler için teşekkürler. Daha fazlasını elde etmek isterim.

NOT: Bu orijinal cevabımdan önemli ölçüde düzenlendi ancak amaç / gereksinimler aynı - bu yüzden yeni bir cevap eklemedim


12
Yazının tamamı için teşekkürler. Ek olarak, IIS7 altında çalışırken "TrySkipIisCustomErrors" özelliğini true olarak ayarlamanız gerekir. Aksi takdirde IIS yine de varsayılan 404 sayfasını döndürür. Response.TrySkipIiisCustomErrors = true; 5. adımda durum kodunu ayarlayan satırdan sonra. msdn.microsoft.com/tr-tr/library/…
Rick

1
@Ryan customErrorsweb.config bölümü, IIS değilse aspnet'te yüksek düzeyde işlenen statik yönlendirme sayfalarını tanımlar. Ben sonuç MVC Views (yani ben onları vb. Olabilir) olması gerektiği gibi bu istediğim değil. Kategorik olarak " customErrorsMVC'de kullanılmıyor " demezdim ama benim için ve bu 404 çözümü kesinlikle öyle.
Matt Kocaj

1
Ayrıca, birisi Adım 3'ü güncelleyebilir, böylece StructureMap kullanılmaz mı? Belki de zaten bir ControllerFactory kullanmıyorsanız uygulaması kolay olacak genel bir ControllerFactory olabilir.
David Murdoch

7
Bu MVC3 için iyi çalışıyor. Bunun yerine ObjectFactory.GetInstanceMVC3'e geçtim, DependencyResolver.Current.GetServicebu yüzden daha genel. Ninject kullanıyorum.
20:11, kamranicus

122
Başka bir web çerçevesi 404 gibi ortak bir şey çok kanlı karmaşık açıkça delilik bulur mu?
quentin-starin

235

ASP.NET MVC, özel 404 sayfaları çok iyi desteklemiyor. Özel denetleyici fabrikası, tümünü yakala rota, temel denetleyici sınıfı ile HandleUnknownAction- ARGH!

IIS özel hata sayfaları şu ana kadar daha iyi bir alternatiftir:

web.config

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
  </httpErrors>
</system.webServer>

ErrorController

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }
}

Örnek Proje


38
BU KABUL EDİLMİŞ CEVAP OLMALIDIR !!! IIS Express ile ASP.NET MVC 3 üzerinde mükemmel çalışır.
Andrei Rînea

7
IIS7 + kullanıyorsanız, kesinlikle bu yol. 1!
elo80ka

3
aynı proje içinde JSON'da çalışırken sadece 404 statüsünü geri döndürmek mümkün mü?
VinnyG

6
Bu iis express'te harika çalışıyor, ancak siteyi üretim IIS 7.5'te dağıttığımda, tüm aldığım hata görünümü yerine beyaz bir sayfa.
Mart'ta Moulde

2
Testlerime göre (MVC3 ile) bu fonksiyonel olmak için customErrors mode="On"birlikte kırılır HandleErrorAttribute. Denetleyici eylemlerindeki işlenmeyen özel durumlar için özel hata sayfaları artık sunulmamaktadır.
Slauma

153

Hızlı Yanıt / TL; DR

resim açıklamasını buraya girin

Dışarıdaki tembel insanlar için:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Ardından bu satırı global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

Ve bu yalnızca IIS7 + ve IIS Express içindir.

Eğer Cassini kullanıyorsanız .. iyi .. um .. er .. garip ... garip


Uzun, açıklanmış cevap

Bunun cevaplandığını biliyorum. Ama cevap GERÇEKTEN BASİT ( David Fowler ve Damian Edwards'a bunu gerçekten cevapladığı için tezahürat ediyor ).

Orada her şey özel yapmaya gerek .

Çünkü ASP.NET MVC3tüm bitler ve parçalar orada.

Adım 1 -> İKİ noktadaki web.config dosyanızı güncelleyin.

<system.web>
    <customErrors mode="On" defaultRedirect="/ServerError">
      <error statusCode="404" redirect="/NotFound" />
    </customErrors>

ve

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

Şimdi kullanmaya karar verdiğim GÜZERGAHLAR'a dikkat edin. Her şeyi kullanabilirsiniz, ancak rotalarım

  • /NotFound <- bulunamadı 404 için hata sayfası.
  • /ServerError<- diğer hatalar için kodumda meydana gelen hataları dahil et. bu 500 Dahili Sunucu Hatası

İlk bölümün nasıl <system.web>yalnızca bir özel girişi olduğunu görüyor musunuz? statusCode="404"Entry? Ben sadece bir durum kodu listeledim çünkü dahil tüm diğer hatalar, 500 Server Error(yani. Kodunuzda bir hata olduğunda ve kullanıcının isteğini çöktüğünde oluşan sinir bozucu hata) .. diğer tüm hatalar ayar tarafından işlenir defaultRedirect="/ServerError".. diyor , bir 404 sayfası bulunamadıysanız, lütfen rotayı seçin /ServerError.

Tamam. bu yoldan çıktı .. şimdi listelenen rotalarımaglobal.asax

Adım 2 - Global.asax'ta rota oluşturma

İşte tam rota bölümüm ..

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

Bu iki göz ardı rotaları listeler -> axd'sve favicons(ooo! İkramiye göz ardı rotası, sizin için!) Sonra (ve sipariş BURADA ZORUNLU olduğunu), benim iki açık hata işleme yolları var .. ve sonra başka herhangi bir yol. Bu durumda, varsayılan olan. Tabii ki, daha fazlasına sahibim, ama bu web siteme özel. Hata yollarının listenin en üstünde olduğundan emin olun. Düzen zorunludur .

Son olarak, global.asaxdosyamızın içindeyken , HandleError özniteliğini global olarak kaydetmiyoruz. Hayır, hayır, efendim. Nadda. Hayır! Nien. Olumsuz. Noooooooooo ...

Bu satırı şuradan kaldır: global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

3. Adım - Denetleyiciyi eylem yöntemleriyle oluşturun

Şimdi iki eylem yöntemiyle bir kontrolör ekliyoruz ...

public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

Tamam, bir bakalım. Her şeyden önce, burada hiçbir [HandleError] özellik yoktur. Neden? Yerleşik ASP.NETçerçeve zaten hataları işlediğinden ve bir hatayı işlemek için yapmamız gereken her şeyi belirttik :) Bu yöntemde!

Sonra, iki eylem yöntemim var. Orada zor bir şey yok. Herhangi bir istisna bilgisi göstermek istiyorsanız, o Server.GetLastError()bilgiyi almak için kullanabilirsiniz .

Bonus WTF: Evet, hata işlemeyi test etmek için üçüncü bir işlem yöntemi yaptım.

4. Adım - Görünümleri Oluşturma

Ve son olarak, iki görünüm oluşturun. Bu denetleyici için onları normal görüntüleme noktasına koyun.

resim açıklamasını buraya girin

Bonus yorumları

  • İhtiyacın yok Application_Error(object sender, EventArgs e)
  • Yukarıdaki adımların hepsi Elmah ile% 100 mükemmel çalışır . Elmah kırma wroxs!

Ve bu, dostlarım, olmalı.

Şimdi, bu kadar okumak için tebrikler ve ödül olarak bir Unicorn var!

resim açıklamasını buraya girin


Bu yüzden bunu uygulamaya çalıştım ama birkaç sorun ... ilk olarak, weeb.config dosyasındaki yoldan önce ~ 'a ihtiyacınız var ya da sanal dizinler için çalışmıyor. 2-IIS özel hataları tetiklenirse ve görünüm hiç oluşturmadığı bir düzen kullanıyorsa, yalnızca beyaz bir sayfa. Ben bu satırı denetleyiciye ekleyerek çözdüm "Response.TrySkipIisCustomErrors = true;" . Ancak, bir dosya olan bir url'ye giderseniz hala işe yaramaz ama 404 .. mysite / whatever / fake.html gibi beyaz bir sayfa alır.
Robert Noack

3
-1, üzgünüm, benim için 404 için url değiştiren herhangi bir çözüm yanlış. ve webconfig ile MVC'de URL'yi değiştirmeden işleyebilmenin bir yolu yoktur veya bunu yapabilmek için statik html dosyaları veya aspx (evet, düz eski aspx dosyaları) oluşturmanız gerekir. URL'lerde olmasını istiyorsanız çözümünüz iyi ?aspxerrorpath=/er/not/found.
Gutek

7
Bu gerçekten garip gelebilir - ama cevabım yıllar önce sağlandı ve @Gutek'inize katılıyorum, artık bir hata sayfasına bir yönlendirme yapmaktan hoşlanmıyorum . Eskiden (cevabımı ref: P). Hata / bazı / kaynak .. meydana geldiyse o zaman bu kaynak bir 404 veya 500, vb döndürmelidir. Aksi takdirde MASSIVE SEO etkileri. Ahh .. nasıl zaman değişir :)
Pure.Krome

@Gutek customErrors redirectMode = "ResponseRewrite" öğesinin farkında mısınız? Ve
404'lerin geri dönüşü

1
@Chris <en sevdiğin ilahı buraya ekle> kahretsin. Şimdi ne olduğunu bile hatırlayamıyorum. Mem koleksiyonum kurtarmaya ... ve .. düzeltildi.
Pure.Krome

86

MVC 404s (özellikle MVC3) düzgün yönetmek için bir ÇOK araştırdım ve bu, IMHO ile geldim en iyi çözümdür:

Global.asax içinde:

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController:

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

(İsteğe bağlı)

Açıklama:

AFAIK, ASP.NET MVC3 uygulamalarının 404s üretebileceği 6 farklı durum vardır.

(ASP.NET Framework tarafından otomatik olarak oluşturulur :)

(1) URL, rota tablosunda bir eşleşme bulamıyor.

(ASP.NET MVC Framework tarafından otomatik olarak oluşturulur :)

(2) Bir URL, rota tablosunda bir eşleşme bulur, ancak var olmayan bir denetleyiciyi belirtir.

(3) Bir URL, rota tablosunda bir eşleşme bulur, ancak mevcut olmayan bir işlemi belirtir.

(Manuel olarak oluşturuldu :)

(4) Bir eylem, HttpNotFound () yöntemini kullanarak bir HttpNotFoundResult döndürür.

(5) Bir eylem, 404 durum koduyla bir HttpException kurar.

(6) Bir eylem, Response.StatusCode özelliğini el ile 404 olarak değiştirir.

Normalde 3 hedefe ulaşmak istiyorsunuz:

(1) Kullanıcıya özel bir 404 hata sayfası gösterin.

(2) İstemci yanıtında 404 durum kodunu koruyun (SEO için özellikle önemlidir).

(3) Yanıtı 302 yönlendirmesi olmadan doğrudan gönderin.

Bunu başarmanın çeşitli yolları vardır:

(1)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

Bu çözümle ilgili sorunlar:

  1. (1), (4), (6) vakalarında objektif (1) ile uyumlu değildir.
  2. Amaç (2) 'ye otomatik olarak uymaz. Manuel olarak programlanmalıdır.
  3. Hedeflere uymuyor (3).

(2)

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Bu çözümle ilgili sorunlar:

  1. Yalnızca IIS 7 ve sonraki sürümlerinde çalışır.
  2. (2), (3), (5) vakalarında objektif (1) ile uyumlu değildir.
  3. Amaç (2) 'ye otomatik olarak uymaz. Manuel olarak programlanmalıdır.

(3)

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Bu çözümle ilgili sorunlar:

  1. Yalnızca IIS 7 ve sonraki sürümlerinde çalışır.
  2. Amaç (2) 'ye otomatik olarak uymaz. Manuel olarak programlanmalıdır.
  3. Uygulama düzeyinde http istisnalarını gizler. Örneğin customErrors bölümünü, System.Web.Mvc.HandleErrorAttribute vb. Kullanamazsınız. Yalnızca genel hata sayfalarını gösteremez.

(4)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

ve

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Bu çözümle ilgili sorunlar:

  1. Yalnızca IIS 7 ve sonraki sürümlerinde çalışır.
  2. Amaç (2) 'ye otomatik olarak uymaz. Manuel olarak programlanmalıdır.
  3. (2), (3), (5) vakalarında objektif (3) ile uyumlu değildir.

Daha önce sorun yaşayan insanlar kendi kütüphanelerini oluşturmaya bile çalıştılar (bkz. Http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html ). Ancak önceki çözüm, harici bir kütüphane kullanmanın karmaşıklığı olmadan tüm vakaları kapsamaktadır.


Mükemmel cevap. Daha birçok upvotes layık. Global.asax kodunuz neden çalışmıyor / Application_Error'a ait değil.
NinjaNye

7
Teşekkürler! Bir denetleyiciden atılan açık 404'ler ASP.NET'te hata olarak değerlendirilmediğinden Application_Error altında yapılamaz. Bir denetleyiciden bir HttpNotFound () döndürürseniz, Application_Error olayı hiçbir zaman tetiklenmez.
Marco

1
Sanýrým public ActionResult NotFound() {}ErrorController'ýný unuttun. Ayrıca, _NotFoundAJAX istekleri için kısmi görünümünüzün nasıl olacağını açıklayabilir misiniz ?
d4n3

2
MVC 4 ile her zaman MissingMethodException: Cannot create an abstract classon line almak c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); Herhangi bir fikir?
µBio

1
"Bulunamadı" URL'si yolda bir nokta içeriyorsa (örn. Example.com/hi.bob ), o zaman Application_EndRequest hiç tetiklenmez ve IE'nin genel 404 sayfasını alıyorum.
Bob.at.Indigo.Health

13

Gerçekten cottsaks çözüm seviyorum ve onun çok açıkladı düşünüyorum. benim tek ekleme adım 2 aşağıdaki gibi değiştirmek oldu

public abstract class MyController : Controller
{

    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        //if controller is ErrorController dont 'nest' exceptions
        if(this.GetType() != typeof(ErrorController))
        this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

Temel olarak bu, geçersiz eylemler VE denetleyicileri içeren URL'lerin istisna rutinini iki kez tetiklemesini durdurur. örneğin asdfsdf / dfgdfgd gibi URL'ler için


4
Bu mükemmel. Bu "iki kez" davalar beni rahatsız etmeye başlamıştı. Cevabımı güncelledi
Matt Kocaj

kullanıcı yanlış denetleyici ve eylem adı girerse yukarıdaki çözüm işe yarar mı?
Monojit Sarkar

6

Geçersiz denetleyiciler için çalışmak için @ cottsak'ın yöntemini alabilmenin tek yolu, CustomControllerFactory'deki varolan rota isteğini değiştirmekti:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType); 
            else
                return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                requestContext.RouteData.Values["controller"] = "Error";
                requestContext.RouteData.Values["action"] = "Http404";
                requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);

                return ObjectFactory.GetInstance<ErrorController>();
            }
            else
                throw ex;
        }
    }
}

MVC 2.0 kullandığımdan bahsetmeliyim.


Neden biliyor musun? (MVC2'ye özgü mü?)
Matt Kocaj

Anahtar yeni bir istek yapmak yerine mevcut isteği değiştirmek olduğunu düşünüyorum, ama bunu bir süre önce yaptım bu yüzden emin değilim. "InvokeHttp404" denetleyici fabrikasından çalışmadı.
Dave K

Cevabımı bugün bazı MVC2 özellikleriyle güncelledim. Yukarıda anlatılan çözümüm hala sizin için işe yaramıyorsa lütfen bana söyler misiniz?
Matt Kocaj

4

MVC araçlarını kullanarak, kötü denetleyici adlarına, bozuk yol adlarına ve bir Eylem yönteminin içine sığdığını gördüğünüz diğer ölçütlere yönelik istekleri işleyebileceğiniz başka bir yöntem. Şahsen, 302/200 yönlendirmesini yaptıkları ve Server.TransferRazor görünümlerini kullanarak ResponseRewrite ( ) yöntemini desteklemedikleri için olabildiğince çok web.config ayarından kaçınmayı tercih ediyorum . SEO nedeniyle 404'ü özel bir hata sayfasıyla döndürmeyi tercih ederim.

Bunlardan bazıları yukarıdaki cottsak tekniğine yeni bir bakış.

Bu çözüm aynı zamanda MVC 3 Hata Filtrelerini tercih eden en az web.config ayarlarını kullanır.

kullanım

Sadece bir eylem veya özel ActionFilterAttribute bir HttpException atın.

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

Aşama 1

Aşağıdaki ayarı web.config dosyanıza ekleyin. MVC'nin HandleErrorAttribute özelliğini kullanmak için bu gereklidir.

<customErrors mode="On" redirectMode="ResponseRedirect" />

Adım 2

HTTP hataları dışında, MVC çerçevesinin HandleErrorAttribute öğesine benzer özel bir HandleHttpErrorAttribute ekleyin:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

Aşama 3

İçindeki GlobalFilterCollection ( GlobalFilters.Filters) öğesine Filtreler ekleyin Global.asax. Bu örnek, tüm InternalServerError (500) hatalarını Hata paylaşılan görünümüne ( Views/Shared/Error.vbhtml) yönlendirecektir. NotFound (404) hataları, paylaşılan görünümlerde ErrorHttp404.vbhtml'ye de gönderilir. Bunun, ek HTTP hata kodları için nasıl genişletilebileceğini göstermek için buraya bir 401 hatası ekledim. Bunların paylaşılan görünümler olması gerektiğini ve hepsinin System.Web.Mvc.HandleErrorInfonesneyi model olarak kullandığını unutmayın.

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

4. Adım

Bir temel denetleyici sınıfı oluşturun ve denetleyicilerinizden devralın. Bu adım, bilinmeyen eylem adlarını işlememize ve HTTP 404 hatasını HandleHttpErrorAttribute'ımıza yükseltmemizi sağlar.

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

Adım 5

ControllerFactory geçersiz kılma oluşturun ve Application_Start içindeki Global.asax dosyanızda geçersiz kılın. Bu adım, geçersiz bir denetleyici adı belirtildiğinde HTTP 404 istisnasını yükseltmemizi sağlar.

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

6. Adım

BaseController Unknown eylemi için RoutTable.Routes özel bir rota ekleyin. Bu, bir kullanıcının bilinmeyen bir denetleyiciye veya bilinmeyen bir eyleme erişmesi durumunda 404'ü yükseltmemize yardımcı olacaktır.

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

özet

Bu örnek, filtre özniteliklerini ve paylaşılan hata görünümlerini kullanarak yeniden yönlendirmeden 404 Http Hata Kodunu tarayıcıya döndürmek için MVC çerçevesini nasıl kullanabileceğini gösterdi. Ayrıca, geçersiz denetleyici adları ve eylem adları belirtildiğinde aynı özel hata sayfasını göstermeyi de gösterir.

Bir = = göndermek için yeterli oy alırsam, geçersiz bir denetleyici adı, eylem adı ve Home / TriggerNotFound eyleminden yükseltilmiş özel bir 404'ün ekran görüntüsünü ekleyeceğim. Bu çözümü kullanarak aşağıdaki URL'lere eriştiğimde Fiddler bir 404 iletisi döndürüyor:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

cottsak'ın gönderisi ve bu makaleler iyi referanslardı.


Hmm, bunun işe yaramasını sağlayamadım: The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'.- bunu neden alacağım konusunda bir fikrin var mı?
enashnash

redirectMode = "ResponseRedirect". Bu 302 Bulunan + SEO için iyi olmayan bir 200 OK dönecektir!
pussinboots

4

İşlenmemiş alanlarla, denetleyicilerle ve eylemlerle çalışan kısaltılmış çözümüm:

  1. 404.cshtml bir görünüm oluşturun.

  2. Denetleyicileriniz için bir temel sınıf oluşturun:

    public class Controller : System.Web.Mvc.Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            Http404().ExecuteResult(ControllerContext);
        }
    
        protected virtual ViewResult Http404()
        {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return View("404");
        }
    }
  3. Temel denetleyicinizi yedek olarak döndüren özel bir denetleyici fabrikası oluşturun:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
  4. Application_Start()Aşağıdaki satıra ekleyin :

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));

3

MVC4 WebAPI 404 aşağıdaki şekilde ele alınabilir,

KURSLAR APICONTROLLER

    // GET /api/courses/5
    public HttpResponseMessage<Courses> Get(int id)
    {
        HttpResponseMessage<Courses> resp = null;

        var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();

        resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);

        return resp;
    }

EV KONTROL CİHAZI

public ActionResult Course(int id)
{
    return View(id);
}

GÖRÜNÜM

<div id="course"></div>
<script type="text/javascript">
    var id = @Model;
    var course = $('#course');
    $.ajax({    
        url: '/api/courses/' + id,
        success: function (data) {
            course.text(data.Name);
        },
        statusCode: {
            404: function() 
            {
                course.text('Course not available!');    
            }
        }
    });
</script>

KÜRESEL

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

SONUÇLAR

resim açıklamasını buraya girin


2

Nuget üzerinde NotFoundMVC'yi deneyin. Çalışıyor, kurulum yok.


http://localhost/Views/Shared/NotFound.cshtmlözel bir 404 sayfasıyla sonuçlanmaz.
Dan Friedman

Özelleştirmek çok kolay. İstediğiniz URL'ye ve yönlendirene erişebilirsiniz, böylece istediğinizi yapabilirsiniz. Bu paketi kullanıyorum ve gerçekten iyi çalışıyor.
Avrohom Yisroel

Bu, zaman uyumsuz Görev <ActionResult> eylemlerini (veya diğer benzer zaman uyumsuz) eylemlerini kullanmayacağınız sürece harika bir pakettir. MVC 5'te bu kırık bir senaryodur. GitHub'da bunu aşmak için bir çatal var, ama benim için hayır, hayır.
Stargazer

2

Benim çözümüm, birisinin faydalı bulması durumunda.

Web.config içinde:

<system.web>
    <customErrors mode="On" defaultRedirect="Error" >
      <error statusCode="404" redirect="~/Error/PageNotFound"/>
    </customErrors>
    ...
</system.web>

İçinde Controllers/ErrorController.cs:

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        if(Request.IsAjaxRequest()) {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return Content("Not Found", "text/plain");
        }

        return View();
    }
}

Bir ekleme PageNotFound.cshtmlde Sharedklasöründe, hepsi bu.


2
Bu, istemciye 302 yönlendirmesinden sonra 200 (Tamam) durumundan kaynaklanmaz mı? Hala 404 statüsü almıyorlar mı?
Sam

@Konamiman emin kodunuzda hat okumalı Are model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ? Request.Url.OriginalString : url;değil model.RequestedUrl = Request.Url.OriginalString.Contains(url) && Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(& yerine &&)?
Jean-François Beauchamp

2

Bana öyle geliyor ki, standart CustomErrorskonfigürasyon sadece çalışmalıdır , buna bağlı olarak Server.Transfer, dahili uygulamanın ResponseRewriteMVC ile uyumlu olmadığı görülmektedir.

Bu bana göze batan bir işlevsellik deliği gibi geliyor, bu yüzden bir HTTP modülü kullanarak bu özelliği yeniden uygulamaya karar verdim. Aşağıdaki çözüm, normal bir şekilde yaptığınız gibi geçerli herhangi bir MVC yoluna yeniden yönlendirerek herhangi bir HTTP durum kodunu (404 dahil) işlemenizi sağlar.

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
    <error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>

Bu, aşağıdaki platformlarda test edilmiştir;

  • Tümleşik Boru Hattı Modunda MVC4 (IIS Express 8)
  • Klasik Modda MVC4 (VS Geliştirme Sunucusu, Cassini)
  • Klasik Modda MVC4 (IIS6)

Yararları

  • Herhangi bir MVC projesine bırakılabilen genel çözüm
  • Geleneksel özel hata yapılandırması için desteği etkinleştirir
  • Hem Entegre Boru Hattı hem de Klasik modlarda çalışır

Çözüm

namespace Foo.Bar.Modules {

    /// <summary>
    /// Enables support for CustomErrors ResponseRewrite mode in MVC.
    /// </summary>
    public class ErrorHandler : IHttpModule {

        private HttpContext HttpContext { get { return HttpContext.Current; } }
        private CustomErrorsSection CustomErrors { get; set; }

        public void Init(HttpApplication application) {
            System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
            CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");

            application.EndRequest += Application_EndRequest;
        }

        protected void Application_EndRequest(object sender, EventArgs e) {

            // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
            if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {

                int statusCode = HttpContext.Response.StatusCode;

                // if this request has thrown an exception then find the real status code
                Exception exception = HttpContext.Error;
                if (exception != null) {
                    // set default error status code for application exceptions
                    statusCode = (int)HttpStatusCode.InternalServerError;
                }

                HttpException httpException = exception as HttpException;
                if (httpException != null) {
                    statusCode = httpException.GetHttpCode();
                }

                if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {

                    Dictionary<int, string> errorPaths = new Dictionary<int, string>();

                    foreach (CustomError error in CustomErrors.Errors) {
                        errorPaths.Add(error.StatusCode, error.Redirect);
                    }

                    // find a custom error path for this status code
                    if (errorPaths.Keys.Contains(statusCode)) {
                        string url = errorPaths[statusCode];

                        // avoid circular redirects
                        if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {

                            HttpContext.Response.Clear();
                            HttpContext.Response.TrySkipIisCustomErrors = true;

                            HttpContext.Server.ClearError();

                            // do the redirect here
                            if (HttpRuntime.UsingIntegratedPipeline) {
                                HttpContext.Server.TransferRequest(url, true);
                            }
                            else {
                                HttpContext.RewritePath(url, false);

                                IHttpHandler httpHandler = new MvcHttpHandler();
                                httpHandler.ProcessRequest(HttpContext);
                            }

                            // return the original status code to the client
                            // (this won't work in integrated pipleline mode)
                            HttpContext.Response.StatusCode = statusCode;

                        }
                    }

                }

            }

        }

        public void Dispose() {

        }


    }

}

kullanım

Bunu web.config dosyasına son HTTP modülü olarak dahil et

  <system.web>
    <httpModules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </httpModules>
  </system.web>

  <!-- IIS7+ -->
  <system.webServer>
    <modules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </modules>
  </system.webServer>

Dikkat edenler için Entegre Boru Hattı modunda bunun çalışma şekli nedeniyle her zaman HTTP 200 ile yanıt vereceğini göreceksiniz Server.TransferRequest. Doğru hata kodunu döndürmek için aşağıdaki hata denetleyicisini kullanıyorum.

public class ErrorController : Controller {

    public ErrorController() { }

    public ActionResult Index(int id) {
        // pass real error code to client
        HttpContext.Response.StatusCode = id;
        HttpContext.Response.TrySkipIisCustomErrors = true;

        return View("Errors/" + id.ToString());
    }

}

2

ASP.NET MVC hataları ile başa çıkmak popo sadece bir acıdır. Bu sayfa ve diğer sorular ve siteler hakkında bir sürü öneri denedim ve hiçbir şey iyi çalışmıyor. Bir öneri, system.webserver içindeki web.config dosyasındaki hataları işlemekti, ancak bu yalnızca boş sayfalar döndürüyor .

Bu çözümü bulduğumda amacım;

  • YÖNLENDİRME DEĞİL
  • Varsayılan hata işleme gibi 200 / Tamam değil UYGUN DURUM KODLARI döndürün

İşte benim çözümüm.

1. system.web bölümüne aşağıdakileri ekleyin

   <system.web>
     <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404"  redirect="~/Error/404.aspx" />
      <error statusCode="500" redirect="~/Error/500.aspx" />
     </customErrors>
    <system.web>

Yukarıdaki, route.config tarafından işlenmeyen URL'leri ve özellikle görünümlerde karşılaşılan işlenmeyen istisnaları ele alır . Uyarı html değil aspx kullandım . Bu yüzden bir arkasındaki kodda yanıt kodu .

2 . Projenizin kökünde Hata (veya istediğinizi seçin) adlı bir klasör oluşturun ve iki web formunu ekleyin. Aşağıda 404 sayfam var;

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title >Page Not found</title>
    <link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
</head>
<body>
    <div class="top-nav">
      <a runat="server" class="company-logo" href="~/"></a>
    </div>
    <div>
        <h1>404 - Page Not found</h1>
        <p>The page you are looking for cannot be found.</p>
        <hr />
        <footer></footer>
    </div>
</body>
</html>

Ve arkasındaki kodda cevap kodunu ayarladım

protected void Page_Load(object sender, EventArgs e)
{
    Response.StatusCode = 404;
}

500 sayfa için de aynısını yapın

3 denetleyicileri içinde .To kolu hataları. Bunu yapmanın birçok yolu var. Bu benim için işe yaradı. Tüm denetleyicilerim bir temel denetleyiciden miras alıyor. Temel denetleyicide, aşağıdaki yöntemlere sahibim

protected ActionResult ShowNotFound()
{
    return ShowNotFound("Page not found....");
}

protected ActionResult ShowNotFound(string message)
{
    return ShowCustomError(HttpStatusCode.NotFound, message);
}

protected ActionResult ShowServerError()
{
    return ShowServerError("Application error....");
}

protected ActionResult ShowServerError(string message)
{
    return ShowCustomError(HttpStatusCode.InternalServerError, message);
}

protected ActionResult ShowNotAuthorized()
{
    return ShowNotAuthorized("You are not allowed ....");

}

protected ActionResult ShowNotAuthorized(string message)
{
    return ShowCustomError(HttpStatusCode.Forbidden, message);
}

protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
{
    Response.StatusCode = (int)statusCode;
    string title = "";
    switch (statusCode)
    {
        case HttpStatusCode.NotFound:
            title = "404 - Not found";
            break;
        case HttpStatusCode.Forbidden:
            title = "403 - Access Denied";
            break;
        default:
            title = "500 - Application Error";
            break;
    }
    ViewBag.Title = title;
    ViewBag.Message = message;
    return View("CustomError");
}

4. CustomError.cshtml dosyasını Paylaşılan görünümler klasörünüze ekleyin . Aşağıda benim;

<h1>@ViewBag.Title</h1>
<br />
<p>@ViewBag.Message</p>

Şimdi uygulama denetleyicinizde böyle bir şey yapabilirsiniz;

public class WidgetsController : ControllerBase
{
  [HttpGet]
  public ActionResult Edit(int id)
  {
    Try
    {
       var widget = db.getWidgetById(id);
       if(widget == null)
          return ShowNotFound();
          //or return ShowNotFound("Invalid widget!");
       return View(widget);
    }
    catch(Exception ex)
    {
       //log error
       logger.Error(ex)
       return ShowServerError();
    }
  }
}

Şimdi uyarı için . Statik dosya hatalarını işlemez. Örneğin, example.com/widgets gibi bir rotanız varsa ve kullanıcı bunu example.com/widgets.html olarak değiştirirse , IIS varsayılan hata sayfasını alırlar, böylece IIS düzeyindeki hataları başka bir şekilde işlemeniz gerekir.


1

Yorumum çok uzun olduğu için cevap gönderiyor ...

Bu tek boynuzlu at gönderi / cevap için hem bir yorum hem de sorular:

https://stackoverflow.com/a/7499406/687549

Bu cevabı, basitliği ve görünüşe göre Microsoft'taki bazı insanlara danışıldığı için diğerlerine tercih ediyorum. Ancak üç soru var ve onlar cevaplanabilir eğer o zaman ben bu cevabı ASP.NET MVC (x) uygulaması için interwebs tüm 404/500 hata cevapların kutsal kâse çağırır.

@ Pure.Krome

  1. Cevabınızı GWB tarafından belirtilen yorumlardan SEO ile güncelleyebilir misiniz (cevabınızda bundan hiç bahsedilmedi) - <customErrors mode="On" redirectMode="ResponseRewrite">ve <httpErrors errorMode="Custom" existingResponse="Replace">?

  2. ASP.NET ekip arkadaşlarınıza böyle yapmanın uygun olup olmadığını sorabilir misiniz - bazı onaylar almak iyi olurdu - belki de büyük bir hayır-hayır değiştirmek redirectModeve existingResponsebu şekilde SEO ile güzel bir şekilde oynayabilmek için ?!

  3. Eğer tüm bu maddeleri çevreleyen bazı konulara açıklık ekleyebilir ( customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", KALDIR customErrorsbirisi önerdi TAMAMEN gibi) Microsoft'ta arkadaşlarına konuştuktan sonra?

Dediğim gibi; 54.000'den fazla görüşe sahip oldukça popüler bir soru gibi göründüğünden cevabınızı daha eksiksiz hale getirebilirsek olağanüstü olurdu.

Güncelleme : Unicorn cevabı 302 Bulundu ve 200 OK yapar ve bir rota kullanarak sadece 404'e dönmek için değiştirilemez. Çok MVC: ish olmayan fiziksel bir dosya olmalıdır. Böylece başka bir çözüme geçelim. Çok kötü çünkü bu nihai MVC: ish cevabı gibi görünüyordu.


1

Herman Kan'ınkiyle neredeyse aynı olan çözümümü, projemde çalışmasına izin vermek için küçük bir kırışıklık ile ekliyoruz.

Özel bir hata denetleyicisi oluşturma:

public class Error404Controller : BaseController
{
    [HttpGet]
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View("404");
    }
}

Ardından özel bir denetleyici fabrikası oluşturun:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
    }
}

Son olarak, özel hata denetleyicisine geçersiz kılma ekleyin:

protected override void HandleUnknownAction(string actionName)
{
    var errorRoute = new RouteData();
    errorRoute.Values.Add("controller", "Error404");
    errorRoute.Values.Add("action", "PageNotFound");
    new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
}

Ve bu kadar. Web.config değişikliğine gerek yoktur.


1

1) Soyut Controller sınıfını yapar.

public abstract class MyController:Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;
        return View("NotFound");
    }

    protected override void HandleUnknownAction(string actionName)
    {
        this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
    }
    protected override void OnAuthorization(AuthorizationContext filterContext) { }
}  

2) Tüm denetleyicilerinizde bu soyut sınıftan miras alın

public class HomeController : MyController
{}  

3) Ve Görünüm Paylaşımlı klasörünüze "NotFound" adlı bir görünüm ekleyin.


0

Bu konuya gönderilen çözümlerin çoğundan geçtim. Bu soru eski olsa da, şu an bile yeni projeler için çok geçerli, bu yüzden burada sunulan cevapları ve başka yerleri okumak için oldukça fazla zaman harcadım.

@Marco, 404'ün gerçekleşebileceği farklı vakalara işaret ettiğinden, birlikte derlediğim çözümü bu listeye göre kontrol ettim. Gereksinimler listesine ek olarak, bir tane daha ekledim.

  • Çözüm, MVC'nin yanı sıra AJAX / WebAPI çağrılarını en uygun şekilde işleyebilmelidir. (ör. 404 MVC'de gerçekleşirse, Bulunamadı sayfasını göstermeli ve 404 WebAPI'de gerçekleşirse, tüketen Javascript'in kolayca ayrıştırabilmesi için XML / JSON yanıtını ele geçirmemelidir).

Bu çözüm 2 kattır:

İlk kısmı https://stackoverflow.com/a/27354140/2310818 adresindeki @Guillaume'den geliyor . Çözümleri, geçersiz yol, geçersiz denetleyici ve geçersiz eylem nedeniyle oluşan 404'ü halleder.

Fikir bir WebForm oluşturmak ve daha sonra MVC Hatalar Denetleyicinizin NotFound eylemi çağırmak yapmaktır. Tüm bunları herhangi bir yönlendirme olmadan yapar, böylece Fiddler'da tek bir 302 görmezsiniz. Orijinal URL de korunur, bu da bu çözümü harika kılar!


İkinci kısmı https://stackoverflow.com/a/5536676/2310818 adresindeki @ Germán'dan geliyor . Onların çözümü HttpNotFoundResult () şeklinde eylemleriniz tarafından döndürülen herhangi bir 404 ile ilgilenir veya yeni HttpException () atın!

Fikir, MVC denetleyicileriniz tarafından atılan istisnanın yanı sıra yanıta bir filtre bakmak ve Hata Denetleyicinizde uygun eylemi çağırmaktır. Yine bu çözüm herhangi bir yönlendirme olmadan çalışır ve orijinal url korunur!


Gördüğünüz gibi, bu çözümlerin her ikisi de birlikte çok sağlam bir hata işleme mekanizması sunuyor ve @Marco tarafından listelenen tüm gereksinimleri ve gereksinimlerimi karşılıyorlar. Bu çözümün çalışan bir örneğini veya bir demosunu görmek istiyorsanız, lütfen yorumlarda bulunun ve bir araya getirmekten memnuniyet duyarız.


0

Tüm makaleleri geçtim ama hiçbir şey benim için işe yaramıyor: Benim gereksinim kullanıcı url özel 404 sayfanızda bir şey göstermelidir.Çok düz olduğunu düşündüm.

 <system.web>
    <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404" redirect="~/PageNotFound.aspx"/>
    </customErrors>
  </system.web>
<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Bu makaleyi çok yararlı buldum. Hemen okunmalıdır. Özel hata sayfası-Ben Foster

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.