Jika diberikan rutenya:
{FeedName} / {ItemPermalink}
mis .: / Blog / Hello-World
Jika item tidak ada, saya ingin mengembalikan 404. Apa cara yang benar untuk melakukan ini di ASP.NET MVC?
Jika diberikan rutenya:
{FeedName} / {ItemPermalink}
mis .: / Blog / Hello-World
Jika item tidak ada, saya ingin mengembalikan 404. Apa cara yang benar untuk melakukan ini di ASP.NET MVC?
Jawaban:
Memotret dari pinggul (pengkodean koboi ;-)), saya menyarankan sesuatu seperti ini:
Pengontrol:
public class HomeController : Controller
{
public ActionResult Index()
{
return new HttpNotFoundResult("This doesn't exist");
}
}
HttpNotFoundResult:
using System;
using System.Net;
using System.Web;
using System.Web.Mvc;
namespace YourNamespaceHere
{
/// <summary>An implementation of <see cref="ActionResult" /> that throws an <see cref="HttpException" />.</summary>
public class HttpNotFoundResult : ActionResult
{
/// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with the specified <paramref name="message"/>.</summary>
/// <param name="message"></param>
public HttpNotFoundResult(String message)
{
this.Message = message;
}
/// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with an empty message.</summary>
public HttpNotFoundResult()
: this(String.Empty) { }
/// <summary>Gets or sets the message that will be passed to the thrown <see cref="HttpException" />.</summary>
public String Message { get; set; }
/// <summary>Overrides the base <see cref="ActionResult.ExecuteResult" /> functionality to throw an <see cref="HttpException" />.</summary>
public override void ExecuteResult(ControllerContext context)
{
throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message);
}
}
}
// By Erik van Brakel, with edits from Daniel Schaffer :)
Dengan menggunakan pendekatan ini, Anda mematuhi standar kerangka kerja. Sudah ada HttpUnauthorizedResult di sana, jadi ini hanya akan memperluas kerangka kerja di mata pengembang lain yang memelihara kode Anda nanti (Anda tahu, psiko yang tahu di mana Anda tinggal).
Anda dapat menggunakan reflektor untuk melihat ke dalam rakitan untuk melihat bagaimana HttpUnauthorizedResult dicapai, karena saya tidak tahu apakah pendekatan ini melewatkan sesuatu (tampaknya hampir terlalu sederhana).
Saya memang menggunakan reflektor untuk melihat HttpUnauthorizedResult sekarang. Sepertinya mereka menyetel StatusCode pada respons ke 0x191 (401). Meskipun ini berfungsi untuk 401, menggunakan 404 sebagai nilai baru saya tampaknya hanya mendapatkan halaman kosong di Firefox. Internet Explorer menunjukkan 404 default (bukan versi ASP.NET). Menggunakan toolbar pengembang web saya memeriksa header di FF, yang menunjukkan respons 404 Not Found. Bisa jadi hanya sesuatu yang saya salah konfigurasi di FF.
Ini dikatakan, saya pikir pendekatan Jeff adalah contoh bagus dari KISS. Jika Anda tidak terlalu membutuhkan verbositas dalam sampel ini, metodenya juga berfungsi dengan baik.
Kami melakukannya seperti itu; kode ini ditemukan diBaseController
/// <summary>
/// returns our standard page not found view
/// </summary>
protected ViewResult PageNotFound()
{
Response.StatusCode = 404;
return View("PageNotFound");
}
disebut seperti itu
public ActionResult ShowUserDetails(int? id)
{
// make sure we have a valid ID
if (!id.HasValue) return PageNotFound();
HttpNotFoundResult adalah langkah pertama yang bagus untuk apa yang saya gunakan. Mengembalikan HttpNotFoundResult itu bagus. Lalu pertanyaannya adalah, selanjutnya apa?
Saya membuat filter tindakan bernama HandleNotFoundAttribute yang kemudian menampilkan halaman kesalahan 404. Karena mengembalikan tampilan, Anda dapat membuat tampilan 404 khusus per pengontrol, atau biarkan menggunakan tampilan 404 bersama default. Ini bahkan akan dipanggil ketika pengontrol tidak memiliki aksi yang ditentukan, karena kerangka kerja melempar HttpException dengan kode status 404.
public class HandleNotFoundAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var httpException = filterContext.Exception.GetBaseException() as HttpException;
if (httpException != null && httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; // Prevents IIS from intercepting the error and displaying its own content.
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound;
filterContext.Result = new ViewResult
{
ViewName = "404",
ViewData = filterContext.Controller.ViewData,
TempData = filterContext.Controller.TempData
};
}
}
}
Perhatikan bahwa pada MVC3, Anda hanya dapat menggunakan HttpStatusCodeResult
.
HttpNotFoundResult
Menggunakan ActionFilter adalah sulit untuk mempertahankan karena setiap kali kita melempar kesalahan kebutuhan filter yang harus ditetapkan dalam atribut. Bagaimana jika kita lupa mengaturnya? Salah satu caranya adalah dengan menurunkan OnException
basis pengontrol. Anda perlu mendefinisikan BaseController
turunan dari Controller
dan semua pengontrol Anda harus berasal dari BaseController
. Ini adalah praktik terbaik untuk memiliki pengontrol dasar.
Catatan jika menggunakan Exception
kode status respon adalah 500, maka kita perlu mengubahnya menjadi 404 untuk Not Found dan 401 untuk Unauthorized. Seperti yang saya sebutkan di atas, gunakan OnException
overrides BaseController
untuk menghindari penggunaan atribut filter.
MVC 3 baru juga membuat lebih merepotkan dengan mengembalikan tampilan kosong ke browser. Solusi terbaik setelah beberapa penelitian didasarkan pada jawaban saya di sini Bagaimana mengembalikan tampilan untuk HttpNotFound () di ASP.Net MVC 3?
Agar lebih nyaman saya tempel di sini:
Setelah belajar. Solusi untuk MVC 3 di sini adalah untuk mendapatkan semua HttpNotFoundResult
, HttpUnauthorizedResult
, HttpStatusCodeResult
kelas dan mengimplementasikan baru (utama itu) HttpNotFound
() metode dalam BaseController
.
Ini adalah praktik terbaik untuk menggunakan Pengontrol dasar sehingga Anda memiliki 'kendali' atas semua Pengontrol turunan.
Saya membuat HttpStatusCodeResult
kelas baru , bukan untuk diturunkan dari ActionResult
tetapi ViewResult
untuk membuat tampilan atau apa pun yang View
Anda inginkan dengan menentukan ViewName
properti. Saya mengikuti yang asli HttpStatusCodeResult
untuk mengatur HttpContext.Response.StatusCode
dan HttpContext.Response.StatusDescription
tetapi kemudian base.ExecuteResult(context)
akan membuat tampilan yang sesuai karena lagi-lagi saya berasal ViewResult
. Cukup sederhana bukan? Semoga ini akan diterapkan di inti MVC.
Lihat di BaseController
bawah saya :
using System.Web;
using System.Web.Mvc;
namespace YourNamespace.Controllers
{
public class BaseController : Controller
{
public BaseController()
{
ViewBag.MetaDescription = Settings.metaDescription;
ViewBag.MetaKeywords = Settings.metaKeywords;
}
protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
{
return new HttpNotFoundResult(statusDescription);
}
protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
{
return new HttpUnauthorizedResult(statusDescription);
}
protected class HttpNotFoundResult : HttpStatusCodeResult
{
public HttpNotFoundResult() : this(null) { }
public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
}
protected class HttpUnauthorizedResult : HttpStatusCodeResult
{
public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
}
protected class HttpStatusCodeResult : ViewResult
{
public int StatusCode { get; private set; }
public string StatusDescription { get; private set; }
public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
public HttpStatusCodeResult(int statusCode, string statusDescription)
{
this.StatusCode = statusCode;
this.StatusDescription = statusDescription;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = this.StatusCode;
if (this.StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = this.StatusDescription;
}
// 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
// 2. Uncomment this and change to any custom view and set the name here or simply
// 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
//this.ViewName = "Error";
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
}
}
Untuk digunakan dalam tindakan Anda seperti ini:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
Dan di _Layout.cshtml (seperti halaman master)
<div class="content">
@if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>@ViewBag.Message</p></div>
}
@RenderBody()
</div>
Selain itu Anda dapat menggunakan tampilan kustom seperti Error.shtml
atau membuat baru NotFound.cshtml
seperti yang saya komentari dalam kode dan Anda dapat menentukan model tampilan untuk deskripsi status dan penjelasan lainnya.