Bagaimana saya bisa menangani 404 dengan benar di ASP.NET MVC?

432

Saya menggunakan RC2

Menggunakan Routing URL:

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

Hal di atas tampaknya menangani permintaan seperti ini (dengan asumsi pengaturan tabel rute default oleh proyek MVC awal): "/ bla / bla / bla / bla"

Mengganti HandleUnknownAction () di controller itu sendiri:

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

Namun strategi sebelumnya tidak menangani permintaan ke pengontrol Buruk / Tidak Dikenal. Sebagai contoh, saya tidak memiliki "/ IDoNotExist", jika saya meminta ini saya mendapatkan halaman 404 generik dari server web dan bukan 404 saya jika saya menggunakan routing + override.

Jadi akhirnya, pertanyaan saya adalah: Apakah ada cara untuk menangkap jenis permintaan ini menggunakan rute atau sesuatu yang lain dalam kerangka kerja MVC itu sendiri?

ATAU haruskah saya default menggunakan Web.Config customErrors sebagai penangan 404 saya dan melupakan semua ini? Saya berasumsi jika saya menggunakan customErrors saya harus menyimpan halaman 404 generik di luar / Tampilan karena pembatasan Web.Config pada akses langsung.

Brian
sumber
3
itu 404 kesalahan, saya tidak akan peduli tentang hal itu. biarkan ditampilkan 404. sebagai pengguna pasti salah ketik. atau jika itu adalah sesuatu yang dipindahkan maka aplikasi Anda harus menerima permintaan itu dan melakukan redirect secara permanen. 404 milik server web bukan aplikasi. Anda selalu dapat menyesuaikan halaman iis untuk kesalahan.
mamu
Anda dapat melihat solusi ini juga blog.dantup.com/2009/04/...
Pengembang
ben.onfabrik.com/posts/aspnet-mvc-custom-error-pages juga memiliki beberapa informasi yang bagus
Chris S
4
Sayang sekali bahwa 4 rilis stabil kemudian dan lebih dari 5 tahun kemudian, situasi untuk menangani 404s di asp.net MVC + IIS belum benar-benar membaik dan ini masih merupakan pertanyaan untuk Q&A bagaimana cara menanganinya.
joelmdev

Jawaban:

271

Kode ini diambil dari http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx dan bekerja di ASP.net MVC 1.0 juga

Inilah cara saya menangani pengecualian http:

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));
}
Shay Jacoby
sumber
23
pembaruan: memeriksa http 404 sudah pasti diperlukan, tetapi saya masih belum yakin kapan Anda akan mendapatkan 500. Anda juga perlu mengatur secara eksplisit Response.StatusCode = 404 atau 500 jika tidak, google akan mulai mengindeks halaman-halaman ini jika Anda mengembalikan kode status 200 yang kode ini saat ini tidak
Simon_Weaver
6
@Simon_Weaver: setuju! Ini perlu mengembalikan 404 kode status. Ini pada dasarnya rusak sebagai solusi 404 sampai benar. Lihat ini: codinghorror.com/blog/2007/03/...
Matt Kocaj
1
Ada kelemahan mendasar dengan seluruh saran ini - pada saat eksekusi telah menggelembung ke Global.asax, terlalu banyak HttpContext hilang. Anda tidak dapat merutekan kembali ke pengontrol seperti yang disarankan contoh. Lihat komentar di tautan blog di atas.
Matt Kocaj
3
Seperti beberapa komentar dari atas dan posting terkait menyebutkan, ini tampaknya tidak berfungsi. Errorcontroller dipukul, tetapi layar kosong kembali. (menggunakan mvc 3)
RyanW
4
ada sesuatu yang terasa tidak beres, seluruh tujuan MVC adalah untuk menghapus semua abstraksi itu, tetapi ini dia lagi ...
Alex Nolasco
255

Persyaratan untuk 404

Berikut ini adalah persyaratan saya untuk solusi 404 dan di bawah ini saya menunjukkan bagaimana saya menerapkannya:

  • Saya ingin menangani rute yang cocok dengan tindakan buruk
  • Saya ingin menangani rute yang cocok dengan pengontrol yang buruk
  • Saya ingin menangani rute yang tidak cocok (url sewenang-wenang yang tidak dapat dipahami oleh aplikasi saya) - saya tidak ingin ini menggelembung ke Global.asax atau IIS karena saya tidak dapat mengarahkan kembali ke aplikasi MVC saya dengan benar
  • Saya ingin cara untuk menangani dengan cara yang sama seperti di atas, custom 404s - seperti ketika ID dikirimkan untuk objek yang tidak ada (mungkin dihapus)
  • Saya ingin semua 404 saya mengembalikan tampilan MVC (bukan halaman statis) di mana saya dapat memompa lebih banyak data nanti jika perlu ( desain 404 bagus ) dan mereka harus mengembalikan kode status HTTP 404

Larutan

Saya pikir Anda harus menyimpan Application_Errordi Global.asax untuk hal-hal yang lebih tinggi, seperti pengecualian dan penebangan yang tidak ditangani (seperti yang ditunjukkan oleh jawaban Shay Jacoby ) tetapi bukan penanganan 404. Inilah sebabnya mengapa saran saya menjaga 404 hal dari file Global.asax.

Langkah 1: Memiliki tempat umum untuk logika 404-kesalahan

Ini adalah ide yang bagus untuk pemeliharaan. Gunakan ErrorController sehingga peningkatan di masa depan untuk halaman 404 Anda yang dirancang dengan baik dapat beradaptasi dengan mudah. Juga, pastikan respons Anda memiliki kode 404 !

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
}

Langkah 2: Gunakan kelas Pengontrol dasar sehingga Anda dapat dengan mudah memanggil tindakan kustom Anda dan memasang HandleUnknownAction

404-an dalam ASP.NET MVC perlu ditangkap di sejumlah tempat. Yang pertama adalah HandleUnknownAction.

The InvokeHttp404Metode menciptakan tempat umum untuk re-routing ke ErrorControllerdan baru Http404bertindak. Pikirkan KERING !

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
}

Langkah 3: Gunakan Injeksi Ketergantungan di Pabrik Pengontrol Anda dan pasang 404 HttpExceptions

Seperti itu (tidak harus StructureMap):

Contoh MVC1.0:

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

Contoh MVC2.0:

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

Saya pikir lebih baik untuk menangkap kesalahan lebih dekat ke tempat asalnya. Inilah mengapa saya lebih suka yang diatas untuk yang Application_Errorhandler.

Ini adalah tempat kedua untuk menangkap 404s.

Langkah 4: Tambahkan rute NotFound ke Global.asax untuk url yang gagal diuraikan ke dalam aplikasi Anda

Rute ini harus mengarah ke Http404tindakan kita . Perhatikan urlparam akan menjadi url relatif karena mesin perutean melucuti bagian domain di sini? Itulah mengapa kita memiliki semua logika url bersyarat pada Langkah 1.

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

Ini adalah tempat ketiga dan terakhir untuk menangkap 404s dalam aplikasi MVC yang tidak Anda gunakan sendiri. Jika Anda tidak menangkap rute yang tidak cocok di sini, maka MVC akan meneruskan masalahnya ke ASP.NET (Global.asax) dan Anda tidak benar-benar menginginkan hal itu dalam situasi ini.

Langkah 5: Akhirnya, aktifkan 404 ketika aplikasi Anda tidak dapat menemukan sesuatu

Seperti ketika ID buruk dikirimkan ke pengontrol Pinjaman saya (berasal dari 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);
    }

Akan lebih baik jika semua ini dapat dihubungkan di tempat yang lebih sedikit dengan kode lebih sedikit tetapi saya pikir solusi ini lebih mudah dikelola, lebih dapat diuji dan cukup pragmatis.

Terima kasih atas responnya sejauh ini. Saya ingin mendapatkan lebih banyak.

CATATAN: Ini telah diedit secara signifikan dari jawaban asli saya tetapi tujuan / persyaratannya sama - ini sebabnya saya belum menambahkan jawaban baru

Matt Kocaj
sumber
12
Terima kasih untuk artikel lengkapnya. Satu tambahan adalah bahwa ketika berjalan di bawah IIS7 Anda perlu menambahkan set properti "TrySkipIisCustomErrors" menjadi true. Kalau tidak IIS masih akan mengembalikan halaman 404 default. Kami menambahkan Response.TrySkipIiisCustomErrors = true; setelah baris pada Langkah 5 yang menetapkan kode status. msdn.microsoft.com/en-us/library/…
Rick
1
@Ryan customErrorsBagian dari web.config mendefinisikan halaman redirect statis yang ditangani pada tingkat tinggi di aspnet jika bukan IIS. Ini bukan yang saya inginkan karena saya harus memiliki hasil menjadi MVC Views (jadi saya dapat memiliki data di dalamnya dll). Saya tidak akan mengatakan dengan tegas bahwa " customErrorssudah usang dalam MVC" tetapi bagi saya dan solusi 404 ini mereka tentu saja.
Matt Kocaj
1
Juga, dapatkah seseorang memperbarui Langkah 3 sehingga StructureMap tidak digunakan? Mungkin hanya ControllerFactory generik yang akan mudah diimplementasikan jika Anda belum menggunakan ControllerFactory.
David Murdoch
7
Ini berfungsi dengan baik untuk MVC3. Saya beralih ObjectFactory.GetInstanceke MVC3 DependencyResolver.Current.GetServicesebagai gantinya jadi lebih umum. Saya menggunakan Ninject.
kamranicus
122
Apakah ada orang lain yang menganggapnya gila bahwa hal biasa seperti 404 dalam kerangka web begitu rumit.
quentin-starin
235

ASP.NET MVC tidak mendukung 404 halaman kustom dengan sangat baik. Pabrik pengontrol khusus, rute tangkap semua, dengan kelas pengontrol basisHandleUnknownAction - argh!

Sejauh ini halaman kesalahan khusus IIS adalah alternatif yang lebih baik:

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

Proyek Sampel

Pavel Chuchuva
sumber
38
INI HARUS MENJADI JAWABAN YANG DITERIMA !!! Bekerja sangat baik di ASP.NET MVC 3 dengan IIS Express.
Andrei Rînea
7
Jika Anda menggunakan IIS7 + ini sudah pasti cara yang harus ditempuh. +1!
elo80ka
3
apakah mungkin untuk mengembalikan hanya status 404 ketika Anda bekerja di JSON dalam proyek yang sama?
VinnyG
6
Ini berfungsi dengan baik di iis express, tetapi segera setelah saya menyebarkan situs dalam produksi IIS 7.5, yang saya dapatkan hanyalah halaman putih alih-alih tampilan kesalahan.
Moulde
2
Menurut tes saya (dengan MVC3) ini rusak customErrors mode="On"bersama dengan HandleErrorAttributemenjadi fungsional. Halaman kesalahan khusus untuk pengecualian yang tidak ditangani dalam tindakan pengontrol tidak dilayani lagi.
Slauma
153

Jawaban Cepat / TL; DR

masukkan deskripsi gambar di sini

Untuk orang-orang malas di luar sana:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Kemudian hapus baris ini dari global.asax

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

Dan ini hanya untuk IIS7 + dan IIS Express.

Jika Anda menggunakan Cassini .. well .. um .. er .. canggung ... canggung


Panjang, jawaban yang dijelaskan

Saya tahu ini telah dijawab. Tetapi jawabannya SANGAT SEDERHANA (sorakan kepada David Fowler dan Damian Edwards untuk benar-benar menjawab ini).

Tidak perlu melakukan kebiasaan apa pun .

Untuk ASP.NET MVC3 , semua potongan ada di sana.

Langkah 1 -> Perbarui web.config Anda di DUA tempat.

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

dan

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

Sekarang perhatikan ROUTES yang telah saya putuskan untuk digunakan. Anda dapat menggunakan apa saja, tetapi rute saya

  • /NotFound <- untuk halaman 404 tidak ditemukan, halaman kesalahan.
  • /ServerError<- untuk kesalahan lain, sertakan kesalahan yang terjadi pada kode saya. ini adalah 500 Server Internal Kesalahan

Lihat bagaimana bagian pertama <system.web>hanya memiliki satu entri khusus? The statusCode="404"entri? Saya hanya mencantumkan satu kode status karena semua kesalahan lainnya, termasuk 500 Server Error(mis. Kesalahan sial yang terjadi ketika kode Anda memiliki bug dan crash permintaan pengguna) .. semua kesalahan lainnya ditangani oleh pengaturan defaultRedirect="/ServerError".. yang mengatakan , jika Anda bukan halaman 404 tidak ditemukan, maka silakan kebagian rute /ServerError.

Baik. itu keluar dari jalan .. sekarang ke rute saya terdaftar diglobal.asax

Langkah 2 - Membuat rute di Global.asax

Inilah bagian rute lengkap saya ..

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

Itu mencantumkan dua rute abaikan -> axd'sdan favicons(ooo! Bonus abaikan rute, untuk Anda!) Lalu (dan urutannya IMPERATIF DI SINI), saya memiliki dua rute penanganan kesalahan yang eksplisit .. diikuti oleh rute lainnya. Dalam hal ini, yang standar. Tentu saja, saya punya lebih banyak, tetapi itu khusus untuk situs web saya. Pastikan rute kesalahan ada di bagian atas daftar. Ketertiban sangat penting .

Akhirnya, sementara kita berada di dalam kita global.asax file kita, kita TIDAK mendaftarkan atribut HandleError secara global. Tidak, tidak, tidak, tuan. Nadda. Nggak. Nien. Negatif. Tidaaaaaak ...

Hapus baris ini dari global.asax

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

Langkah 3 - Buat controller dengan metode tindakan

Sekarang .. kita tambahkan controller dengan dua metode aksi ...

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

Ok, mari kita periksa ini. Pertama-tama, tidak ada [HandleError] atribut di sini. Mengapa? Karena built inASP.NET kerangka kerja bawaan sudah menangani kesalahan DAN kami telah menentukan semua omong kosong yang perlu kami lakukan untuk menangani kesalahan :) Ada dalam metode ini!

Selanjutnya, saya memiliki dua metode tindakan. Tidak ada yang sulit di sana. Jika Anda ingin menampilkan info pengecualian, maka Anda dapat menggunakannya Server.GetLastError()untuk mendapatkan info itu.

Bonus WTF: Ya, saya membuat metode tindakan ketiga, untuk menguji penanganan kesalahan.

Langkah 4 - Buat Views

Dan akhirnya, buat dua tampilan. Tempatkan mereka di tempat tampilan normal, untuk pengontrol ini.

masukkan deskripsi gambar di sini

Komentar bonus

  • Anda tidak membutuhkan Application_Error(object sender, EventArgs e)
  • Langkah-langkah di atas semuanya bekerja 100% sempurna dengan Elmah . Elmah memalsukan wrox!

Dan itu, teman-teman saya, seharusnya begitu.

Sekarang, selamat telah membaca sebanyak ini dan dapatkan Unicorn sebagai hadiah!

masukkan deskripsi gambar di sini

Murni
sumber
Jadi saya mencoba untuk mengimplementasikan ini tetapi beberapa masalah ... pertama, Anda perlu ~ sebelum jalan di weeb.config atau tidak berfungsi untuk direktori virtual. 2-Jika kesalahan kustom IIS menyala dan tampilan menggunakan tata letak itu tidak membuat sama sekali, hanya halaman putih. Saya memecahkannya dengan menambahkan baris ini ke controller "Response.TrySkipIisCustomErrors = true;" . Namun, itu masih tidak berfungsi jika Anda membuka url yang merupakan file tetapi 404 .. seperti mysite / whatever / fake.html mendapat halaman putih.
Robert Noack
3
-1, maaf, bagi saya solusi apa pun yang mengubah url untuk 404 salah. dan dengan webconfig tidak ada cara di MVC yang dapat Anda tangani tanpa mengubah url, atau Anda perlu membuat file html statis atau aspx (ya, file aspx lama biasa) untuk dapat melakukannya. solusi Anda baik-baik saja jika Anda suka ?aspxerrorpath=/er/not/foundada di url.
Gutek
7
Ini mungkin terdengar sangat aneh - tetapi jawaban saya diberikan sejak lama dan saya setuju dengan @Gutek Anda, saya tidak suka melakukan pengalihan ke halaman kesalahan lagi . Saya dulu (ref untuk jawaban saya: P). Jika kesalahan terjadi pada / beberapa / sumber daya .. maka sumber daya YANG harus mengembalikan 404 atau 500, dll implikasi SEO BESAR sebaliknya. Ahh .. bagaimana kali berubah :)
Pure.Krome
@Gutek Apakah Anda mengetahui customErrors redirectMode = "ResponseRewrite"? Dan mengembalikan 404 tidak ideal dari perspektif keamanan
Jowen
1
@ Chris <masukkan dewa favoritmu di sini> sial. Aku bahkan tidak ingat apa itu sekarang. Nah, koleksi meme saya untuk menyelamatkan ... dan .. diperbaiki.
Pure.Krome
86

Saya telah menyelidiki BANYAK tentang cara mengelola 404 dengan benar di MVC (khususnya MVC3) , dan ini, IMHO adalah solusi terbaik yang saya buat:

Di global.asax:

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

(Pilihan)

Penjelasan:

AFAIK, ada 6 kasus berbeda yang dapat dihasilkan oleh aplikasi ASP.NET MVC3 404s.

(Secara otomatis dihasilkan oleh ASP.NET Framework :)

(1) URL tidak menemukan kecocokan di tabel rute.

(Secara otomatis dihasilkan oleh ASP.NET MVC Framework :)

(2) URL menemukan kecocokan dalam tabel rute, tetapi menentukan pengontrol yang tidak ada.

(3) URL menemukan kecocokan di tabel rute, tetapi menentukan tindakan yang tidak ada.

(Dibuat secara manual :)

(4) Suatu tindakan mengembalikan HttpNotFoundResult dengan menggunakan metode HttpNotFound ().

(5) Suatu tindakan melempar HttpException dengan kode status 404.

(6) Suatu tindakan secara manual memodifikasi properti Response.StatusCode ke 404.

Biasanya, Anda ingin mencapai 3 tujuan:

(1) Tampilkan halaman kesalahan 404 khusus untuk pengguna.

(2) Pertahankan kode status 404 pada respons klien (khusus penting untuk SEO).

(3) Kirim tanggapan secara langsung, tanpa melibatkan pengalihan 302.

Ada berbagai cara untuk mencapai hal ini:

(1)

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

Masalah dengan solusi ini:

  1. Tidak sesuai dengan tujuan (1) dalam kasus (1), (4), (6).
  2. Tidak sesuai dengan tujuan (2) secara otomatis. Itu harus diprogram secara manual.
  3. Tidak sesuai dengan tujuan (3).

(2)

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

Masalah dengan solusi ini:

  1. Hanya berfungsi di IIS 7+.
  2. Tidak sesuai dengan tujuan (1) dalam kasus (2), (3), (5).
  3. Tidak sesuai dengan tujuan (2) secara otomatis. Itu harus diprogram secara manual.

(3)

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

Masalah dengan solusi ini:

  1. Hanya berfungsi di IIS 7+.
  2. Tidak sesuai dengan tujuan (2) secara otomatis. Itu harus diprogram secara manual.
  3. Ini mengaburkan pengecualian tingkat aplikasi http. Misalnya tidak dapat menggunakan bagian customErrors, System.Web.Mvc.HandleErrorAttribute, dll. Itu tidak hanya dapat menampilkan halaman kesalahan umum.

(4)

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

dan

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

Masalah dengan solusi ini:

  1. Hanya berfungsi di IIS 7+.
  2. Tidak sesuai dengan tujuan (2) secara otomatis. Itu harus diprogram secara manual.
  3. Tidak sesuai dengan tujuan (3) dalam kasus (2), (3), (5).

Orang-orang yang bermasalah dengan ini sebelumnya bahkan mencoba membuat perpustakaan mereka sendiri (lihat http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html ). Tetapi solusi sebelumnya tampaknya mencakup semua kasus tanpa kerumitan menggunakan perpustakaan eksternal.

Marco
sumber
Jawaban yang bagus Layak banyak lagi upvotes. Mengapa kode global.asax Anda tidak berfungsi / termasuk dalam Application_Error.
NinjaNye
7
Terima kasih! Itu tidak dapat dilakukan di bawah Application_Error, karena 404s eksplisit yang dilemparkan dari controller tidak dianggap kesalahan pada ASP.NET. Jika Anda mengembalikan HttpNotFound () dari controller, peristiwa Application_Error tidak akan pernah memicu.
Marco
1
Saya pikir Anda lupa public ActionResult NotFound() {}di ErrorsController Anda. Juga, dapatkah Anda menjelaskan _NotFoundseperti apa tampilan parsial Anda untuk permintaan AJAX?
d4n3
2
Dengan MVC 4, saya selalu MissingMethodException: Cannot create an abstract classterhubung. c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); Ada ide?
μBio
1
Jika URL "tidak ditemukan" menyertakan titik di jalur (misalnya example.com/hi.bob ) maka Application_EndRequest tidak menyala sama sekali, dan saya mendapatkan halaman 404 umum IE.
Bob.at.Indigo.Health
13

Saya sangat suka solusi cottsaks dan berpikir dengan sangat jelas. Satu-satunya tambahan saya adalah mengubah langkah 2 sebagai berikut

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
}

Pada dasarnya ini berhenti url berisi tindakan yang tidak valid dan pengontrol memicu rutin pengecualian dua kali. misalnya untuk url seperti asdfsdf / dfgdfgd

Dave Lowe
sumber
4
Ini luar biasa. Kasus "dua kali" itu mulai mengganggu saya. memperbarui jawaban saya
Matt Kocaj
apakah solusi di atas berfungsi sama sekali jika pengguna memasukkan nama pengontrol dan tindakan yang salah?
Monojit Sarkar
6

Satu-satunya cara saya bisa mendapatkan metode @ cottsak untuk bekerja untuk pengontrol yang tidak valid adalah dengan memodifikasi permintaan rute yang ada di CustomControllerFactory, seperti:

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

Saya harus menyebutkan saya menggunakan MVC 2.0.

Dave K.
sumber
Apa kamu tahu kenapa? (Khusus MVC2?)
Matt Kocaj
Saya pikir kuncinya adalah untuk memodifikasi permintaan yang ada daripada membuat yang baru, tapi saya melakukan ini beberapa waktu yang lalu jadi saya tidak yakin itu saja. "InvokeHttp404" tidak berfungsi dari pabrik pengontrol.
Dave K
Saya telah memperbarui jawaban saya hari ini dengan beberapa spesifikasi MVC2. Bisakah Anda memberi tahu saya jika solusi saya seperti yang dijelaskan di atas masih tidak bekerja untuk Anda?
Matt Kocaj
4

Berikut adalah metode lain menggunakan alat MVC yang Anda dapat menangani permintaan untuk nama pengontrol yang buruk, nama rute yang buruk, dan kriteria lain yang Anda anggap cocok di dalam metode Action. Secara pribadi, saya lebih suka menghindari pengaturan web.config sebanyak mungkin, karena mereka melakukan redirect 302/200 dan tidak mendukung ResponseRewrite ( Server.Transfer) menggunakan tampilan Razor. Saya lebih suka mengembalikan 404 dengan halaman kesalahan khusus untuk alasan SEO.

Beberapa di antaranya adalah teknik baru cottsak di atas.

Solusi ini juga menggunakan pengaturan web.config minimal yang mendukung Filter Kesalahan MVC 3.

Pemakaian

Hanya melempar HttpException dari suatu tindakan atau ActionFilterAttribute kustom.

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

Langkah 1

Tambahkan pengaturan berikut ke web.config Anda. Ini diperlukan untuk menggunakan HandleErrorAttribute MVC.

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

Langkah 2

Tambahkan kustom HandleHttpErrorAttribute mirip dengan HandleErrorAttribute kerangka kerja MVC, kecuali untuk kesalahan HTTP:

<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

Langkah 3

Tambahkan Filter ke GlobalFilterCollection ( GlobalFilters.Filters) di Global.asax. Contoh ini akan merutekan semua kesalahan InternalServerError (500) ke tampilan Kesalahan bersama ( Views/Shared/Error.vbhtml). Kesalahan NotFound (404) akan dikirim ke ErrorHttp404.vbhtml juga dalam tampilan bersama. Saya telah menambahkan kesalahan 401 di sini untuk menunjukkan kepada Anda bagaimana ini dapat diperluas untuk kode kesalahan HTTP tambahan. Perhatikan bahwa ini harus dibagi pandangan, dan mereka semua menggunakan System.Web.Mvc.HandleErrorInfoobjek sebagai model.

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

Langkah 4

Buat kelas pengontrol dasar dan mewarisinya dari pengontrol Anda. Langkah ini memungkinkan kami untuk menangani nama tindakan yang tidak dikenal dan meningkatkan kesalahan HTTP 404 ke HandleHttpErrorAttribute kami.

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

Langkah 5

Buat override ControllerFactory, dan override dalam file Global.asax Anda di Application_Start. Langkah ini memungkinkan kami untuk meningkatkan pengecualian HTTP 404 ketika nama pengontrol yang tidak valid telah ditentukan.

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)

Langkah 6

Sertakan rute khusus di RoutTable Anda. Rute untuk aksi BaseController Tidak Diketahui. Ini akan membantu kami menaikkan 404 jika pengguna mengakses pengontrol yang tidak dikenal, atau tindakan yang tidak dikenal.

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

Ringkasan

Contoh ini menunjukkan bagaimana seseorang dapat menggunakan kerangka kerja MVC untuk mengembalikan 404 Kode Kesalahan Http ke browser tanpa pengalihan menggunakan atribut filter dan tampilan kesalahan bersama. Itu juga menunjukkan menampilkan halaman kesalahan khusus yang sama ketika nama pengontrol yang tidak valid dan nama tindakan ditentukan.

Saya akan menambahkan tangkapan layar dari nama pengontrol yang tidak valid, nama tindakan, dan custom 404 yang dimunculkan dari tindakan Home / TriggerNotFound jika saya mendapatkan cukup suara untuk memposting satu =). Fiddler mengembalikan pesan 404 ketika saya mengakses URL berikut menggunakan solusi ini:

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

posting cottsak di atas dan artikel ini adalah referensi yang bagus.

langit-dev
sumber
Hmm, saya tidak bisa membuat ini bekerja: The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'.- ada ide mengapa saya mendapatkannya?
enashnash
redirectMode = "ResponseRedirect". Ini akan mengembalikan 302 Ditemukan + 200 OK yang tidak baik untuk SEO!
PussInBoots
4

Solusi singkat saya yang bekerja dengan area, pengendali, dan tindakan yang tidak tertangani:

  1. Buat tampilan 404.cshtml.

  2. Buat kelas dasar untuk pengontrol Anda:

    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. Buat pabrik pengontrol khusus yang mengembalikan pengontrol dasar Anda sebagai cadangan:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
  4. Tambahkan ke Application_Start()baris berikut:

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
Herman Kan
sumber
3

Dalam MVC4 WebAPI 404 dapat ditangani dengan cara berikut,

APICONTROLLER KURSUS

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

KONTROLER RUMAH

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

MELIHAT

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

GLOBAL

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

HASIL

masukkan deskripsi gambar di sini

Diganta Kumar
sumber
2

Coba NotFoundMVC di nuget. Berhasil, tidak ada pengaturan.

DarthVader
sumber
http://localhost/Views/Shared/NotFound.cshtmltidak menghasilkan halaman 404 khusus.
Dan Friedman
Sangat mudah untuk menyesuaikan. Anda memiliki akses ke url yang diminta dan pengarah, sehingga Anda dapat melakukan apa yang Anda suka. Saya menggunakan paket ini, dan berfungsi dengan sangat baik.
Avrohom Yisroel
Ini adalah paket yang bagus, asalkan Anda tidak akan menggunakan tindakan Tugas async <ActionResult> (atau async serupa lainnya). Pada MVC 5 ini adalah skenario yang rusak. Ada garpu di GitHub untuk menghindari itu, tetapi bagi saya itu adalah tidak, tidak.
Stargazer
2

Solusi saya, kalau-kalau ada yang merasa berguna.

Di Web.config:

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

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

Tambahkan PageNotFound.cshtmldalam Sharedfolder, dan hanya itu.

Konamiman
sumber
2
Bukankah ini mengeluarkan 302 redirect maka status 200 (OK) ke klien? Bukankah seharusnya mereka masih mendapatkan status 404?
Sam
@Konamiman Anda yakin baris dalam kode Anda harus dibaca model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ? Request.Url.OriginalString : url;dan bukan model.RequestedUrl = Request.Url.OriginalString.Contains(url) && Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(& bukan &&)?
Jean-François Beauchamp
2

Sepertinya saya bahwa CustomErrorskonfigurasi standar hanya akan berfungsi , karena ketergantungan pada Server.Transfertampaknya implementasi internalResponseRewrite tidak kompatibel dengan MVC.

Ini terasa seperti lubang fungsionalitas yang mencolok bagi saya, jadi saya memutuskan untuk mengimplementasikan kembali fitur ini menggunakan modul HTTP. Solusi di bawah ini memungkinkan Anda untuk menangani kode status HTTP apa pun (termasuk 404) dengan mengarahkan ulang ke rute MVC yang valid seperti yang akan Anda lakukan secara normal.

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

Ini telah diuji pada platform berikut;

  • MVC4 dalam Mode Pipeline Terpadu (IIS Express 8)
  • MVC4 dalam Mode Klasik (VS Development Server, Cassini)
  • MVC4 dalam Mode Klasik (IIS6)

Manfaat

  • Solusi umum yang dapat dijatuhkan ke proyek MVC apa pun
  • Mengaktifkan dukungan untuk konfigurasi kesalahan khusus tradisional
  • Berfungsi dalam mode Pipa Terpadu dan Klasik

Solusinya

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() {

        }


    }

}

Pemakaian

Sertakan ini sebagai modul HTTP final di web.config Anda

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

Bagi Anda yang memperhatikan, Anda akan melihat bahwa dalam mode Pipeline Terpadu ini akan selalu merespons dengan HTTP 200 karena cara Server.TransferRequestkerjanya. Untuk mengembalikan kode kesalahan yang tepat saya menggunakan pengontrol kesalahan berikut.

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

}
Red Taz
sumber
2

Berurusan dengan kesalahan dalam ASP.NET MVC hanya menyebalkan. Saya mencoba banyak saran di halaman ini dan pada pertanyaan dan situs lain dan tidak ada yang berhasil. Satu saran adalah untuk menangani kesalahan pada web.config di dalam system.webserver tetapi itu hanya mengembalikan halaman kosong .

Tujuan saya ketika datang dengan solusi ini adalah untuk;

  • TIDAK MENURUNKAN
  • Kembalikan STATUS PROPER CODES tidak 200 / OK seperti penanganan kesalahan standar

Ini solusinya.

1 .Add berikut untuk system.web bagian

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

Di atas menangani url apa pun yang tidak ditangani oleh route.config dan pengecualian tidak tertangani terutama yang ditemui pada tampilan. Perhatikan saya menggunakan aspx bukan html . Ini agar saya dapat menambahkan kode respons pada kode di belakang.

2 . Buat folder bernama Kesalahan (atau apa pun yang Anda inginkan) di root proyek Anda dan tambahkan dua bentuk web. Di bawah ini adalah halaman 404 saya;

<%@ 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>

Dan pada kode di belakang saya mengatur kode respons

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

Lakukan hal yang sama untuk halaman 500

3. Untuk menangani kesalahan dalam pengontrol. Ada banyak cara untuk melakukannya. Inilah yang bekerja untuk saya. Semua pengendali saya mewarisi dari pengendali basis. Di pengontrol dasar, saya memiliki metode berikut

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. Tambahkan CustomError.cshtml ke folder Shared views Anda. Di bawah ini milik saya;

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

Sekarang di pengontrol aplikasi Anda, Anda dapat melakukan sesuatu seperti ini;

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

Sekarang untuk peringatan . Itu tidak akan menangani kesalahan file statis. Jadi jika Anda memiliki rute seperti example.com/widgets dan pengguna mengubahnya menjadi example.com/widgets.html , mereka akan mendapatkan halaman kesalahan default IIS sehingga Anda harus menangani kesalahan level IIS dengan cara lain.

Musa Machua
sumber
1

Posting jawaban karena komentar saya terlalu panjang ...

Ini adalah komentar dan pertanyaan untuk postingan / jawaban unicorn:

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

Saya lebih suka jawaban ini daripada yang lain karena kesederhanaannya dan fakta bahwa beberapa orang di Microsoft dikonsultasikan. Saya punya tiga pertanyaan dan jika mereka bisa dijawab maka saya akan menyebut jawaban ini sebagai grail suci dari semua 404/500 jawaban kesalahan pada jalinan untuk aplikasi ASP.NET MVC (x).

@ Pure.Krome

  1. Bisakah Anda memperbarui jawaban Anda dengan hal-hal SEO dari komentar yang ditunjukkan oleh GWB (tidak pernah ada yang menyebutkan hal ini dalam jawaban Anda) - <customErrors mode="On" redirectMode="ResponseRewrite">dan <httpErrors errorMode="Custom" existingResponse="Replace">?

  2. Bisakah Anda bertanya kepada teman-teman tim ASP.NET Anda apakah boleh melakukannya seperti itu - akan menyenangkan jika ada konfirmasi - mungkin itu adalah tidak-tidak besar untuk berubah redirectModedan existingResponsedengan cara ini untuk dapat bermain dengan baik dengan SEO ?!

  3. Anda dapat menambahkan beberapa klarifikasi seputar semua hal ( customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", HAPUS customErrorsSama Sekali sebagai seseorang disarankan) setelah berbicara dengan teman-teman Anda di Microsoft?

Seperti yang saya katakan; itu akan menjadi supernice jika kami dapat membuat jawaban Anda lebih lengkap karena ini tampaknya menjadi pertanyaan yang cukup populer dengan 54.000 tampilan.

Pembaruan : Unicorn answer melakukan 302 Found dan 200 OK dan tidak dapat diubah untuk hanya mengembalikan 404 menggunakan rute. Itu harus berupa file fisik yang tidak terlalu MVC: ish. Jadi beralih ke solusi lain. Sayang sekali karena ini sepertinya menjadi MVC pamungkas: ish jawab sejauh ini.

Kucing dalam sepatu
sumber
1

Menambahkan solusi saya, yang hampir identik dengan Herman Kan, dengan kerutan kecil agar dapat bekerja untuk proyek saya.

Buat pengontrol kesalahan khusus:

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

Kemudian buat pabrik pengontrol khusus:

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

Akhirnya, tambahkan override ke pengontrol kesalahan khusus:

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

Dan itu saja. Tidak perlu untuk perubahan Web.config.

Rob Lyndon
sumber
1

1) Buat kelas Pengontrol abstrak.

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) Buat warisan dari kelas abstrak ini di semua pengontrol Anda

public class HomeController : MyController
{}  

3) Dan tambahkan tampilan bernama "NotFound" di folder View-Shared Anda.

Mehmet
sumber
0

Saya telah melalui sebagian besar solusi yang diposting di utas ini. Walaupun pertanyaan ini mungkin sudah lama, masih sangat berlaku untuk proyek-proyek baru bahkan sekarang, jadi saya menghabiskan banyak waktu membaca jawaban yang disajikan di sini dan juga di mana.

Ketika @Marco menunjukkan berbagai kasus di mana 404 dapat terjadi, saya memeriksa solusi yang saya susun bersama terhadap daftar itu. Selain daftar persyaratannya, saya juga menambahkan satu lagi.

  • Solusinya harus dapat menangani panggilan MVC serta AJAX / WebAPI dengan cara yang paling tepat. (yaitu jika 404 terjadi di MVC, itu akan menampilkan halaman Tidak Ditemukan dan jika 404 terjadi di WebAPI, seharusnya tidak membajak respons XML / JSON sehingga Javascript yang mengkonsumsi dapat menguraikannya dengan mudah).

Solusi ini 2 kali lipat:

Bagian pertama berasal dari @Guillaume di https://stackoverflow.com/a/27354140/2310818 . Solusi mereka menangani 404 apa pun yang disebabkan karena rute tidak valid, pengontrol tidak valid, dan tindakan tidak sah.

Idenya adalah untuk membuat WebForm dan kemudian membuatnya memanggil tindakan NotFound dari Pengontrol Kesalahan MVC Anda. Itu melakukan semua ini tanpa redirect sehingga Anda tidak akan melihat 302 tunggal di Fiddler. URL asli juga dipertahankan, yang membuat solusi ini fantastis!


Bagian kedua darinya berasal dari @ Germán di https://stackoverflow.com/a/5536676/2310818 . Solusi mereka menangani setiap 404 yang dikembalikan oleh tindakan Anda dalam bentuk HttpNotFoundResult () atau melempar HttpException baru ()!

Idenya adalah memiliki tampilan filter pada respons serta pengecualian yang dilemparkan oleh pengontrol MVC Anda dan untuk memanggil tindakan yang sesuai di Pengontrol Kesalahan Anda. Sekali lagi solusi ini berfungsi tanpa redirect dan url asli tetap dipertahankan!


Seperti yang Anda lihat, kedua solusi ini bersama-sama menawarkan mekanisme penanganan kesalahan yang sangat kuat dan mereka mencapai semua persyaratan yang tercantum oleh @Marco serta persyaratan saya. Jika Anda ingin melihat sampel yang berfungsi atau demo dari solusi ini, silakan tinggalkan di komentar dan saya akan senang untuk menyatukannya.

Parth Shah
sumber
0

Saya telah membaca semua artikel tetapi tidak ada yang berhasil untuk saya: Pengguna saya harus mengetikkan apa saja di halaman 404 kustom url Anda. Saya pikir ini sangat mudah. ​​Tapi Anda harus memahami penanganan 404 dengan benar:

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

Saya menemukan artikel ini sangat membantu. Harus dibaca sekaligus. Halaman kesalahan Custome-Ben Foster

Thakur Rock
sumber