ASP.NET MVC कार्रवाई से HTTP 404 प्रतिक्रिया भेजने का उचित तरीका क्या है?


92

यदि मार्ग दिया गया है:

{FeedName} / {} ItemPermalink

ex: / ब्लॉग / हैलो-वर्ल्ड

यदि आइटम मौजूद नहीं है, तो मैं 404 वापस करना चाहता हूं। ASP.NET MVC में ऐसा करने का सही तरीका क्या है?


यह सवाल पूछने के लिए धन्यवाद btw। यह मेरे मानक परियोजना परिवर्धन में जा रहा है: डी
एरिक वैन ब्रैकेल

जवाबों:


69

कूल्हे से शूटिंग (चरवाहा कोडिंग; ;-)), मैं कुछ इस तरह का सुझाव दूंगा:

नियंत्रक:

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

इस दृष्टिकोण का उपयोग करके आप फ्रेमवर्क मानकों का पालन करते हैं। वहां पहले से ही एक HttpUnauthorizedResult मौजूद है, इसलिए यह बस बाद में आपके कोड को बनाए रखने वाले किसी अन्य डेवलपर की आंखों में रूपरेखा का विस्तार करेगा (आप जानते हैं, वह साइको जो जानता है कि आप कहां रहते हैं)।

HttpUnauthorizedResult को कैसे प्राप्त किया जाता है यह देखने के लिए आप असेंबली में एक नज़र डालने के लिए उपयोग कर सकते हैं, क्योंकि मुझे नहीं पता कि क्या यह दृष्टिकोण कुछ भी याद करता है (यह बहुत सरल लगता है)।


मैंने अभी-अभी HttpUnauthorizedResult पर एक नज़र डालने के लिए रिफ्लेक्टर का उपयोग किया। लगता है कि वे 0x191 (401) की प्रतिक्रिया पर StatusCode सेट कर रहे हैं। हालाँकि यह 401 के लिए काम करता है, लेकिन नए मूल्य के रूप में 404 का उपयोग करके मुझे फ़ायरफ़ॉक्स में सिर्फ एक खाली पृष्ठ मिल रहा है। Internet Explorer डिफ़ॉल्ट 404 दिखाता है (ASP.NET संस्करण नहीं)। Webdeveloper टूलबार का उपयोग करके मैंने FF में हेडर का निरीक्षण किया, जो कि एक 404 Not Found प्रतिक्रिया को दर्शाता है। एफएफ में मुझे गलत तरीके से बताई गई कुछ चीजें हो सकती हैं।


यह किया जा रहा है ने कहा, मुझे लगता है कि जेफ के दृष्टिकोण KISS का एक अच्छा उदाहरण है। यदि आपको वास्तव में इस नमूने में वाचालता की आवश्यकता नहीं है, तो उसकी विधि ठीक काम करती है।


हाँ, मैंने Enum पर भी ध्यान दिया। जैसा कि मैंने कहा, यह सिर्फ एक क्रूड उदाहरण है, इसे सुधारने के लिए स्वतंत्र महसूस करें। यह सभी के बाद एक नॉलेजबेस माना जाता है ;-)
एरिक वैन ब्रकेल

मुझे लगता है कि मैं थोड़ा आगे निकल गया ... आनंद लें: डी
डैनियल शेफ़र

एफडब्ल्यूआईडब्ल्यू, जेफ के उदाहरण के लिए यह भी आवश्यक है कि आपके पास एक कस्टम 404 पेज हो।
डैनियल शेफ़र

2
केवल HttpContext.Response.StatusCode = 404 सेट करने के बजाय HttpException को फेंकने के साथ एक समस्या यह है कि यदि आप OnException नियंत्रक हैंडलर (जैसा कि मैं करते हैं) का उपयोग करते हैं, तो यह HttpException भी पकड़ लेगा। इसलिए मुझे लगता है कि सिर्फ स्टेटसकोड सेट करना एक बेहतर तरीका है।
इगोर ब्रेक्ज

4
MVC3 में HttpException या HttpNotFoundResult कई मायनों में उपयोगी है। @ आईजीआर ब्रेजक के मामले में, यदि ऑनफैसेप्शन में स्टेटमेंट में त्रुटि नहीं मिली है , तो उसका उपयोग करें ।
CallMeLaNN

46

हम इसे पसंद करते हैं; इस कोड में पाया जाता हैBaseController

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

ऐसा कहा जाता है

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

क्या यह क्रिया फिर एक डिफ़ॉल्ट मार्ग तक पहुंच गई है? यह नहीं देख सकता कि इसे कैसे अंजाम दिया जाए।
क्रिश्चियन दलगर

2
इसे इस तरह से चलाया जा सकता है: संरक्षित ओवरराइड void HandleUnognAction (स्ट्रिंग एक्शननाम) {PageNotFound ()। ExecuteResult (यह ।ControllerContext); }
ट्रिस्टन वार्नर-स्मिथ

मैं इसे इस तरह से करता था, लेकिन पाया कि परिणाम को विभाजित करना और प्रदर्शित दृश्य बेहतर दृष्टिकोण था। नीचे मेरे जवाब की जाँच करें।
ब्रायन वेलेलुंगा

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

मुझे यह पसंद है क्योंकि यह स्थापित किए गए कस्टम त्रुटि पृष्ठों का अनुसरण करता है web.config
माइक कोल

7

HttpNotFoundResult एक महान पहला कदम है जो मैं उपयोग कर रहा हूं। एक HttpNotFoundResult लौटना अच्छा है। फिर सवाल यह है कि आगे क्या है?

मैंने HandleNotFoundAttribute नामक एक एक्शन फ़िल्टर बनाया, जो तब 404 एरर पेज दिखाता है। चूंकि यह एक दृश्य लौटाता है, आप प्रति नियंत्रक 404 दृश्य बना सकते हैं, या डिफ़ॉल्ट रूप से साझा किए गए 404 दृश्य का उपयोग कर सकते हैं। यह तब भी कहा जाएगा जब किसी नियंत्रक के पास निर्दिष्ट क्रिया मौजूद नहीं है, क्योंकि फ्रेमवर्क 404 की स्थिति कोड के साथ एक HttpException फेंकता है।

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


6

ActionFilter का उपयोग करना मुश्किल है क्योंकि जब भी हम एक त्रुटि फेंकते हैं तो फ़िल्टर को विशेषता में सेट करने की आवश्यकता होती है। क्या होगा अगर हम इसे सेट करना भूल जाते हैं? एक तरीका OnExceptionबेस कंट्रोलर पर आ रहा है। आपको एक BaseControllerव्युत्पन्न से परिभाषित करने की आवश्यकता है Controllerऔर आपके सभी नियंत्रकों से प्राप्त होना चाहिएBaseController । बेस कंट्रोलर होना सबसे अच्छा अभ्यास है।

ध्यान दें कि यदि Exceptionप्रतिक्रिया स्थिति कोड 500 का उपयोग कर रहा है, तो हमें इसे 404 में नहीं मिला और अनधिकृत के लिए 401 में बदलना होगा। जैसे मैं ऊपर उल्लेख करता हूं, OnExceptionओवरराइड्स का उपयोग करेंBaseController फ़िल्टर विशेषता का उपयोग करने से बचने के का उपयोग करें।

नया MVC 3 भी ब्राउज़र के लिए एक खाली दृश्य लौटाकर अधिक परेशानी पैदा करता है। कुछ शोध के बाद सबसे अच्छा समाधान यहाँ मेरे जवाब पर आधारित है। एएसटी.नेट एमवीसी 3 में HttpNotFound () के लिए एक दृश्य कैसे वापस करें?

और अधिक विश्वास बनाने के लिए मैं इसे यहाँ प्रस्तुत करता हूँ:


कुछ अध्ययन के बाद। के लिए वैकल्पिक हल MVC 3 यहाँ सभी प्राप्त करने के लिए है HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResultवर्गों और लागू नई (यह अधिभावी) HttpNotFound() में विधि BaseController

बेस कंट्रोलर का उपयोग करना सबसे अच्छा अभ्यास है ताकि आप सभी व्युत्पन्न नियंत्रकों पर 'नियंत्रण' रखें।

मैं नई HttpStatusCodeResultश्रेणी बनाता हूं , न कि प्राप्त करने के लिए, ActionResultलेकिन ViewResultउस दृश्य या किसी भी संपत्ति Viewको निर्दिष्ट करके जो आप चाहते ViewNameहैं। मैं HttpStatusCodeResultसेट करने के लिए मूल का पालन करता हूं HttpContext.Response.StatusCodeऔर HttpContext.Response.StatusDescriptionफिर base.ExecuteResult(context)उपयुक्त दृश्य प्रदान करूंगा क्योंकि फिर से मैं प्राप्त करता हूं ViewResult। बहुत सरल है? उम्मीद है कि इसे एमवीसी कोर में लागू किया जाएगा।

मेरा BaseControllerबोला देखें :

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

namespace YourNamespace.Controllers
{
    public class BaseController : Controller
    {
        public BaseController()
        {
            ViewBag.MetaDescription = Settings.metaDescription;
            ViewBag.MetaKeywords = Settings.metaKeywords;
        }

        protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
        {
            return new HttpNotFoundResult(statusDescription);
        }

        protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
        {
            return new HttpUnauthorizedResult(statusDescription);
        }

        protected class HttpNotFoundResult : HttpStatusCodeResult
        {
            public HttpNotFoundResult() : this(null) { }

            public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }

        }

        protected class HttpUnauthorizedResult : HttpStatusCodeResult
        {
            public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
        }

        protected class HttpStatusCodeResult : ViewResult
        {
            public int StatusCode { get; private set; }
            public string StatusDescription { get; private set; }

            public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }

            public HttpStatusCodeResult(int statusCode, string statusDescription)
            {
                this.StatusCode = statusCode;
                this.StatusDescription = statusDescription;
            }

            public override void ExecuteResult(ControllerContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }

                context.HttpContext.Response.StatusCode = this.StatusCode;
                if (this.StatusDescription != null)
                {
                    context.HttpContext.Response.StatusDescription = this.StatusDescription;
                }
                // 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
                // 2. Uncomment this and change to any custom view and set the name here or simply
                // 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
                //this.ViewName = "Error";
                this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
                base.ExecuteResult(context);
            }
        }
    }
}

इस तरह से अपनी कार्रवाई में उपयोग करने के लिए:

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

और _Layout.cshtml (मास्टर पेज की तरह)

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

इसके अतिरिक्त आप कस्टम दृश्य का उपयोग कर सकते हैं जैसे Error.shtmlनया बना सकते हैं NotFound.cshtmlजैसे मैंने कोड में टिप्पणी की है और आप स्थिति विवरण और अन्य स्पष्टीकरण के लिए एक दृश्य मॉडल परिभाषित कर सकते हैं।


आप हमेशा एक वैश्विक फ़िल्टर पंजीकृत कर सकते हैं जो एक आधार नियंत्रक को धड़कता है क्योंकि आपको अपने आधार नियंत्रक का उपयोग करने के लिए REMEMBER करना पड़ता है!
जॉन कुल्विनर

:) यकीन नहीं या तो यह अभी भी MVC4 में एक मुद्दा है। उस समय मेरे कहने का मतलब है किसी और के द्वारा दिया गया हैंडेलनॉटफ़ाउंड एट्रिब्यूट फ़िल्टर। प्रत्येक क्रिया के लिए आवेदन किया जाना आवश्यक नहीं है। उदा। यह केवल कार्रवाई के लिए उपयुक्त है जिसमें आईडी परम है लेकिन सूचकांक () कार्रवाई नहीं है। मैं वैश्विक फ़िल्टर पर सहमत हूं, हैंडलेनॉटफ़ाउंड एट्रिब्यूट के लिए नहीं बल्कि एक कस्टम हैंडलेयरऑउट श्रद्धांजलि के लिए।
CallMeLaNN

मुझे लगा कि MVC3 के पास भी यह सुनिश्चित है। दूसरों की परवाह किए बिना अच्छी चर्चा जो जवाब में आ सकती है
जॉन कुलविनर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.