如何在ASP.NET MVC中正确处理404?


432

我正在使用RC2

使用网址路由:

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

上面的代码似乎处理了这样的请求(假设由初始MVC项目设置了默认路由表):“ / blah / blah / blah / blah”

在控制器本身中重写HandleUnknownAction():

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

但是,先前的策略无法处理对错误/未知控制器的请求。例如,我没有“ / IDoNotExist”,如果我请求此请求,则从Web服务器获取通用404页面,如果使用路由+覆盖,则无法获取我的404页面。

所以最后,我的问题是: 是否可以使用MVC框架本身中的路由或其他方法来捕获此类请求?

还是我应该默认使用Web.Config customErrors作为我的404处理程序,而忘记所有这些?我假设如果我使用customErrors,由于直接访问的Web.Config限制,我将不得不将通用404页面存储在/ Views之外。


3
这是404错误,我只是不会理会它。让它显示404。因为用户肯定输入了错误的内容。或者,如果这是移动的内容,则您的应用程序应该接受该请求并进行永久重定向。404属于Web服务器而非应用程序。您可以随时为错误自定义iis页面。
mamu 2010年

您也可以查看此解决方案blog.dantup.com/2009/04/…–
开发人员


4
遗憾的是,在4个稳定版本之后以及5年以上的时间里发布,asp.net MVC + IIS中处理404的情况并没有真正改善,这仍然是如何处理它的问答环节。
2014年

Answers:


271

该代码摘自http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx,并且可以正常工作在ASP.net MVC 1.0中也是如此

这是我处理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));
}

23
更新:绝对需要检查http 404,但是我仍然不确定何时获得500。此外,您还需要明确设置Response.StatusCode = 404或500,否则Google将开始为这些页面建立索引如果您要返回此状态代码当前执行的200状态代码
Simon_Weaver 2009年

6
@Simon_Weaver:同意!这需要返回404状态代码。它实际上是作为404解决方案破坏的,直到它解决了。检查:codinghorror.com/blog/2007/03/...
马特Kocaj

1
整个建议存在一个潜在的缺陷-到执行力上升到Global.asax时,缺少了很多HttpContext。您不能像示例所建议的那样路由回到控制器中。请参阅顶部博客链接中的评论。
Matt Kocaj

3
正如上面的一些评论和链接的文章所述,这似乎不起作用。错误控制器已命中,但返回黑屏。(使用mvc 3)
RyanW 2011年

4
某些感觉不对的地方,MVC的全部目的是删除所有这些抽象,但是这里又回来了……
Alex Nolasco

255

404要求

以下是我对404解决方案的要求,下面说明如何实现该解决方案:

  • 我想通过不良行为处理匹配的路线
  • 我想用错误的控制器处理匹配的路由
  • 我想处理不匹配的路由(我的应用无法理解的任意网址)-我不希望这些冒泡到Global.asax或IIS,因为那样我就无法正确地重定向回我的MVC应用
  • 我想要一种以与上述相同的方式来处理自定义404的方法-就像为不存在的对象(可能已删除)提交ID时一样
  • 我希望我所有的404都返回一个MVC视图(而不是静态页面),以后可以在必要时向其泵入更多数据(好的404设计),并且它们必须返回HTTP 404状态代码

我认为您应该Application_Error在Global.asax中保存更多内容,例如未处理的异常和日志记录(例如Shay Jacoby的答案显示),而不是404处理。这就是为什么我的建议将404内容保留在Global.asax文件中的原因。

第1步:对404错误逻辑有共同的看法

这是可维护性的好主意。使用ErrorController,以使将来对精心设计的404页的改进可以轻松适应。另外,请确保您的回复中包含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
}

第2步:使用基本Controller类,以便您可以轻松调用自定义404操作并进行连接 HandleUnknownAction

ASP.NET MVC中的404需要在许多地方被捕获。首先是HandleUnknownAction

InvokeHttp404方法为重新路由到ErrorController和我们的新Http404操作创建了一个通用位置。觉得

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
}

步骤3:在Controller Factory中使用依赖注入并连接404 HttpExceptions

像这样(不必是StructureMap):

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

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

我认为最好在错误发生的地方更接近错误。这就是为什么我更喜欢上面的Application_Error处理程序。

这是捕获404的第二个地方。

步骤4:为无法解析到您的应用程序的网址向Global.asax添加一个NotFound路由

这条路线应该指向我们的Http404行动。注意,url由于路由引擎在此处剥离域部分,因此参数将是相对URL。这就是为什么我们在步骤1中拥有所有条件url逻辑的原因。

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

这是在您自己不调用的MVC应用程序中捕获404的第三个也是最后一个位置。如果您在这里没有找到不匹配的路由,那么MVC会将问题传递给ASP.NET(Global.asax),在这种情况下您实际上并不希望这样做。

第5步:最后,在您的应用找不到内容时调用404

就像将错误的ID提交给我的“贷款”控制器一样(源自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);
    }

如果所有这些都可以用更少的代码挂接到更少的地方,那将是很好的选择,但是我认为这种解决方案更易于维护,更可测试并且相当实用。

感谢到目前为止的反馈。我想得到更多。

注意:这已经从我的原始答案中进行了重大编辑,但是目的/要求是相同的-这就是为什么我没有添加新答案


12
感谢您的完整撰写。一种附加功能是,当在IIS7下运行时,需要添加将属性“ TrySkipIisCustomErrors”设置为true。否则,IIS仍将返回默认的404页面。我们添加了Response.TrySkipIiisCustomErrors = true; 在第5步中设置状态代码的行之后。 msdn.microsoft.com/en-us/library/...
里克

1
@Ryan customErrorsweb.config 的部分定义了静态重定向页面,如果不是IIS,则在aspnet中进行高层处理。这不是我想要的,因为我需要使结果成为MVC视图(以便我可以在其中包含数据等)。我不会断然地说“ customErrors在MVC中已过时”,但对于我和这个404解决方案而言,它们肯定是。
马特·科卡伊

1
另外,有人可以更新第3步,以便不使用StructureMap吗?如果您还没有使用ControllerFactory,也许只是一个易于实现的通用ControllerFactory。
大卫·默多克

7
这对于MVC3正常工作。我改用ObjectFactory.GetInstance了MVC3,DependencyResolver.Current.GetService所以它更通用。我正在使用Ninject。
kamranicus 2011年

122
还有没有人发现Web框架中的404之类的普通事情如此血腥而复杂,显然是疯了吗?
quentin-starin 2012年

235

ASP.NET MVC不能很好地支持自定义404页面。定制控制器工厂,全面路线,基础控制器类别HandleUnknownAction-Argh!

到目前为止,IIS自定义错误页面是更好的选择:

web.config

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

错误控制器

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

样例项目


38
这应该已经被接受了!!!在带有IIS Express的ASP.NET MVC 3上运行良好。
2011年

7
如果您使用的是IIS7 +,那么绝对是正确的方法。+1!
elo80ka 2011年

3
当您在同一项目中使用JSON工作时,是否只能返回状态404?
VinnyG

6
这在iis express中工作得很好,但是一旦我在生产IIS 7.5中部署了站点,我得到的就是白页而不是错误视图。
Moulde 2012年

2
根据我的测试(使用MVC3),此功能customErrors mode="On"与一起中断HandleErrorAttribute。不再提供针对控制器操作中未处理的异常的自定义错误页面。
Slauma 2012年

153

快速解答/ TL; DR

在此处输入图片说明

对于那里的懒惰人:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

然后从 global.asax

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

这仅适用于IIS7 +和IIS Express。

如果您使用的是卡西尼(Cassini)..嗯..嗯..呃..尴尬... 尴尬


解释得很长的答案

我知道已经回答了。但是答案是非常简单的(向David FowlerDamian Edwards表示感谢)。

没有必要做任何定制

对于ASP.NET MVC3,所有点点滴滴都在那里。

步骤1->在两个位置更新您的web.config。

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

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

现在,请注意我决定使用的路由。你可以用任何东西,但是我的路线是

  • /NotFound <-对于404找不到错误页面。
  • /ServerError<-对于其他任何错误,包括我的代码中发生的错误。这是一个500内部服务器错误

看看第一部分<system.web>只有一个自定义项吗?该statusCode="404"项目吗?我只列出了一个状态代码,因为所有其他错误,包括500 Server Error(例如,当您的代码有错误并导致用户请求崩溃时发生的那些令人讨厌的错误)..所有其他错误均由设置处理defaultRedirect="/ServerError"。 ,如果找不到404页面,请转到路线/ServerError

好。到现在为止,我的路线已列出global.asax

第2步-在Global.asax中创建路由

这是我的完整路线部分。

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

那列出了两个忽略路由-> axd'sfavicons(哦!额外的忽略路由,为您服务!)然后(顺序为IMPERATIVE HERE),我有两个显式的错误处理路由..之后是其他任何路由。在这种情况下,默认值是1。当然,我还有更多,但这对我的网站来说很特殊。只要确保错误路由在列表的顶部即可。秩序势在必行

最后,当我们在里面 global.asax文件内时,我们不会全局注册HandleError属性。不,不,不,先生。娜达 不。念 负。不...

从中删除此行 global.asax

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

步骤3-使用操作方法创建控制器

现在..我们添加了具有两种动作方法的控制器...

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

好的,让我们检查一下。首先,这里没有 [HandleError]属性。为什么?因为内置ASP.NET框架已经在处理错误,并且我们已经指定了处理错误所需的所有操作:)就在这种方法中!

接下来,我有两种动作方法。那里没什么难的。如果您希望显示任何异常信息,则可以Server.GetLastError()用来获取该信息。

奖励WTF:是的,我提出了第三个操作方法,以测试错误处理。

第4步-创建视图

最后,创建两个视图。将em放置在该控制器的常规视点中。

在此处输入图片说明

奖励评论

  • 您不需要 Application_Error(object sender, EventArgs e)
  • 以上步骤对Elmah都可以100%完美地工作。埃尔玛吓坏了!

我的朋友们应该就是这样。

现在,恭喜您阅读了很多,并获得了独角兽奖!

在此处输入图片说明


因此,我尝试实现此功能,但遇到了一些问题……首先,您需要在weeb.config中的路径之前输入〜,否则它不适用于虚拟目录。2-如果IIS自定义错误触发,并且视图使用的布局根本无法呈现,则仅呈现白页。通过将这一行添加到控制器“ Response.TrySkipIisCustomErrors = true;”中,我解决了该问题。。但是,如果您访问的是文件但不是404的网址,则仍然无法正常工作。例如mysite / whatever / fake.html 会获得白页。
罗伯特·诺亚克

3
-1,对不起,对我来说,任何确实更改404网址的解决方案都是错误的。并且使用webconfig,在MVC中无法在不更改url的情况下对其进行处理,或者您需要创建静态html文件或aspx(是的,普通的旧aspx文件)才能执行此操作。如果您希望?aspxerrorpath=/er/not/found包含网址,则您的解决方案很好。
Gutek

7
这听起来似乎很奇怪-但是我的答案是很久以前提供的,并且我同意您的@Gutek,我不希望重定向到错误页面。我曾经(参考我的回答:P)。如果错误发生在/ some / resource ..上,则该资源应返回404或500,等等。啊..时代如何改变:)
Pure.Krome

@Gutek您知道customErrors redirectMode =“ ResponseRewrite”吗?从安全角度来看,返回404并不理想
Jowen

1
@Chris <在这里插入您最喜欢的神>该死。我什至不记得现在是什么。好吧,我的模因集合可以营救...和..固定的。
Pure.Krome,2016年

86

我研究了很多有关如何正确管理MVC (尤其是MVC3)中的 404的信息,而IMHO是我提出的最佳解决方案:

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

(可选的)

说明:

AFAIK,ASP.NET MVC3应用程序可以在6种不同情况下生成404。

(由ASP.NET Framework自动生成:)

(1) URL在路由表中找不到匹配项。

(由ASP.NET MVC框架自动生成:)

(2) URL在路由表中找到匹配项,但指定了不存在的控制器。

(3) URL在路由表中找到匹配项,但指定了不存在的动作。

(手动生成:)

(4)动作使用方法HttpNotFound()返回HttpNotFoundResult。

(5)动作引发状态代码为404的HttpException。

(6)一个操作手动将Response.StatusCode属性修改为404。

通常,您要实现3个目标:

(1)向用户显示一个自定义的404错误页面。

(2)在客户端响应上保留404状态代码(对于SEO特别重要)。

(3)直接发送响应,而不涉及302重定向。

有多种方法可以尝试实现此目的:

(1)

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

该解决方案存在的问题:

  1. 在情况(1),(4),(6)中不符合目标(1)。
  2. 不自动符合目标(2)。必须手动编程。
  3. 不符合目标(3)。

(2)

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

该解决方案存在的问题:

  1. 仅适用于IIS 7+。
  2. 在情况(2),(3),(5)中不符合目标(1)。
  3. 不自动符合目标(2)。必须手动编程。

(3)

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

该解决方案存在的问题:

  1. 仅适用于IIS 7+。
  2. 不自动符合目标(2)。必须手动编程。
  3. 它掩盖了应用程序级别的http异常。例如,不能使用customErrors部分,System.Web.Mvc.HandleErrorAttribute等。它不能仅显示常规错误页面。

(4)

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

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

该解决方案存在的问题:

  1. 仅适用于IIS 7+。
  2. 不自动符合目标(2)。必须手动编程。
  3. 在情况(2),(3),(5)中不符合目标(3)。

在此之前遇到麻烦的人甚至尝试创建自己的库(请参阅http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html)。但是以前的解决方案似乎涵盖了所有情况,而没有使用外部库的复杂性。


好答案。值得更多支持。为什么您的global.asax代码在Application_Error中不起作用/所属。
NinjaNye 2012年

7
谢谢!在Application_Error下无法完成此操作,因为从控制器抛出的显式404不被视为ASP.NET上的错误。如果从控制器返回HttpNotFound(),则Application_Error事件将永远不会触发。
马可(Marco)

1
我认为您忘记public ActionResult NotFound() {}了ErrorsController。另外,您能否解释_NotFound一下AJAX请求的部分外观如何?
d4n3 2012年

2
使用MVC 4,我可以始终保持MissingMethodException: Cannot create an abstract class在线状态有c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); 什么想法?
µBio 2012年

1
如果“未找到” URL的路径中包含点(例如example.com/hi.bob),则Application_EndRequest根本不会触发,我得到了IE的通用404页面。
Bob.at.Indigo.Health 2014年

13

我非常喜欢cottsaks解决方案,并认为它的解释很清楚。我唯一的添加是如下更改步骤2

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
}

基本上,这将阻止包含无效操作和控制器的URL两次触发异常例程。例如用于asdfsdf / dfgdfgd之类的网址


4
太好了 那些“两次”案件开始困扰我。更新了我的答案
马特·科卡伊

如果用户输入了错误的控制器和动作名称,上述解决方案将完全起作用吗?
Monojit Sarkar,

6

我可以使@cottsak的方法适用于无效控制器的唯一方法是修改CustomControllerFactory中的现有路由请求,如下所示:

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。


你知道为什么吗?(特定于MVC2?)
Matt Kocaj 2010年

我认为关键是修改现有请求,而不是提出新请求,但是我前一阵子这样做了,所以我不确定是不是这样。在控制器工厂中,“ InvokeHttp404”无效。
Dave K

我今天用一些MVC2细节更新了我的答案。您能告诉我上面详述的解决方案是否仍然不适合您吗?
Matt Kocaj 2011年

4

这是使用MVC工具的另一种方法,可以处理对错误的控制器名称,错误的路由名称以及您认为适合于Action方法的任何其他条件的请求。就我个人而言,我宁愿避免尽可能多的web.config设置,因为它们执行302/200重定向,并且不支持Server.Transfer使用Razor视图的ResponseRewrite()。由于SEO的原因,我希望返回带有自定义错误页面的404。

其中一些是对cottsak的技术的新尝试。

该解决方案还使用最少的web.config设置,而使用MVC 3错误过滤器。

用法

只需从操作或自定义ActionFilterAttribute中抛出HttpException即可。

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

步骤1

将以下设置添加到您的web.config。这是使用MVC的HandleErrorAttribute所必需的。

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

第2步

添加类似于MVC框架的HandleErrorAttribute的自定义HandleHttpErrorAttribute,除了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

第三步

在中将过滤器添加到GlobalFilterCollection(GlobalFilters.FiltersGlobal.asax。本示例将所有InternalServerError(500)错误路由到Error共享视图(Views/Shared/Error.vbhtml)。NotFound(404)错误也将在共享视图中发送到ErrorHttp404.vbhtml。我在此处添加了401错误,以向您展示如何将其扩展为其他HTTP错误代码。请注意,这些必须是共享视图,并且它们都将System.Web.Mvc.HandleErrorInfo对象用作模型。

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

第四步

创建一个基本控制器类,并在您的控制器中继承它。此步骤使我们能够处理未知的操作名称,并将HTTP 404错误引发到我们的HandleHttpErrorAttribute。

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

第5步

创建一个ControllerFactory覆盖,并在Application_Start的Global.asax文件中覆盖它。当指定了无效的控制器名称时,此步骤使我们可以引发HTTP 404异常。

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步

在您的RoutTable.Routes中包含一个特殊的路由,用于BaseController Unknown操作。这将帮助我们在用户访问未知控制器或未知操作的情况下引发404。

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

摘要

此示例演示了如何使用MVC框架将404 Http错误代码返回到浏览器,而无需使用过滤器属性和共享错误视图进行重定向。当指定了无效的控制器名称和操作名称时,它还演示了显示相同的自定义错误页面。

如果我获得足够的票数来张贴一个=),我将添加一个无效的控制器名称,操作名称以及从Home / TriggerNotFound操作引发的自定义404的屏幕截图。当我使用此解决方案访问以下URL时,Fiddler返回404消息:

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

以上的cottsak帖子和这些文章是很好的参考。


嗯,我无法解决这个问题:The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'.-为什么我会明白这一点?
enashnash 2012年

redirectMode =“ ResponseRedirect”。这将返回302 Found + 200 OK,这对SEO不利!
PussInBoots

4

我的简化解决方案适用于未处理的区域,控制器和操作:

  1. 创建一个视图404.cshtml。

  2. 为您的控制器创建一个基类:

    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. 创建一个自定义控制器工厂,以将您的基本控制器作为后备返回:

    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()以下行:

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

3

在MVC4中,可以通过以下方式处理WebAPI 404:

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

家用控制器

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

视图

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

全球

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

结果

在此处输入图片说明


2

在nuget上尝试NotFoundMVC。它有效,无需设置。


http://localhost/Views/Shared/NotFound.cshtml不会生成自定义404页面。
丹·弗里德曼

定制非常容易。您可以访问所请求的URL和引荐来源,因此您可以执行自己喜欢的操作。我使用了这个程序包,并且效果很好。
Avrohom Yisroel 2015年

这是一个很棒的程序包,只要您不使用异步Task <ActionResult>操作(或其他类似的async)操作即可。在MVC 5上,这是一个坏情况。GitHub上有一个分支来规避这一点,但是对我来说,这是一个不,不。
观星者

2

我的解决方案,以防有人觉得有用。

在Web.config中:

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

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

PageNotFound.cshtmlShared文件夹中添加一个,仅此而已。


2
这不是向客户端发出302重定向然后200(OK)状态的问题吗?他们不应该还处于404状态吗?
山姆

@Konamiman您确定代码中的行应读为 model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ? Request.Url.OriginalString : url;而不是model.RequestedUrl = Request.Url.OriginalString.Contains(url) && Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(&而不是&&)?
让·弗朗索瓦·比彻姆

2

在我看来,标准CustomErrors配置应该可以使用,但是由于依赖于Server.Transfer它的内部实现似乎ResponseRewrite与MVC不兼容。

这对我来说似乎是一个巨大的功能漏洞,因此我决定使用HTTP模块重新实现此功能。下面的解决方案使您可以像平常一样通过重定向到任何有效的MVC路由来处理任何HTTP状态代码(包括404)。

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

已在以下平台上进行了测试;

  • 集成管道模式下的MVC4(IIS Express 8)
  • 经典模式下的MVC4(VS开发服务器,卡西尼号)
  • 经典模式下的MVC4(IIS6)

好处

  • 可以放入任何MVC项目的通用解决方案
  • 支持传统的自定义错误配置
  • 在集成管道模式和经典模式下均可工作

解决方案

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

        }


    }

}

用法

将其作为web.config中的最终HTTP模块

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

对于那些关注的人,您会注意到在集成管道模式下,由于Server.TransferRequest工作方式的原因,它将始终以HTTP 200响应。为了返回正确的错误代码,我使用以下错误控制器。

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中的错误只是一团糟。我在此页面以及其他问题和站点上尝试了很多建议,但都没有很好的效果。一种建议是处理system.webserverweb.config上的错误,但这只会返回空白页

我提出此解决方案的目的是:

  • 不重定向
  • 像默认错误处理一样,返回正确的状态码而不是200 / OK

这是我的解决方案。

1。将以下内容添加到system.web部分

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

上面的代码处理了route.config和未处理的异常未处理的所有url ,尤其是视图中遇到的异常。注意,我用的不是aspx的html。这样,我可以在后面的代码上添加响应代码

2。在项目的根目录创建一个名为Error的文件夹(或任何您喜欢的文件夹),然后添加两个Web表单。以下是我的404页面;

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

然后在后面的代码中设置响应代码

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

对500页执行相同的操作

3。处理控制器内的错误。有很多方法可以做到这一点。这对我有用。我所有的控制器都继承自基本控制器。在基本控制器中,我有以下方法

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添加到“ 共享视图”文件夹中。下面是我的;

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

现在,您可以在应用程序控制器中执行以下操作:

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

现在请注意。它不会处理静态文件错误。因此,如果您拥有诸如example.com/widgets之类的路由,并且用户将其更改为example.com/widgets.html,他们将获得IIS默认错误页面,因此您必须以其他方式处理IIS级别错误。


1

由于我的评论过长,因此发布答案...

对独角兽帖子/答案既有评论又有疑问:

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

与简单的答案相比,我更喜欢此答案,因为它很简单,而且事实上已经咨询了Microsoft的某些人员。但是,我遇到了三个问题,如果可以回答,那么我将把这个答案称为ASP.NET MVC(x)应用程序在Internet上所有404/500错误答案的圣杯。

@ Pure.Krome

  1. 您能否从GWB指出的评论中使用SEO内容更新您的答案(答案中从未提及)- <customErrors mode="On" redirectMode="ResponseRewrite">并且<httpErrors errorMode="Custom" existingResponse="Replace">

  2. 您能问一下ASP.NET团队的朋友是否可以那样做-可以得到一些确认-可能需要更改,redirectMode并且existingResponse可以与SEO很好地玩,这是一个很大的禁忌吗?

  3. 你可以添加一些澄清周围所有的东西(customErrors redirectMode="ResponseRewrite"customErrors redirectMode="ResponseRedirect"httpErrors errorMode="Custom" existingResponse="Replace",REMOVE customErrors完全地有人建议),微软说你的朋友后?

正如我所说; 如果我们可以使您的答案更加完整,那将是一件很荒谬的事情,因为这似乎是一个颇受欢迎的问题,拥有54000多个视图。

更新:Unicorn的答案是302 Found和200 OK,并且不能更改为仅使用路线返回404。它必须是不是非常MVC:ish的物理文件。因此,转到另一个解决方案。太糟糕了,因为到目前为止这似乎是最终的MVC:ish答案。


1

添加我的解决方案(几乎与Herman Kan的解决方案相同),并进行细微调整以使其适用于我的项目。

创建一个自定义错误控制器:

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

然后创建一个自定义控制器工厂:

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

最后,向自定义错误控制器添加替代:

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

就是这样。无需更改Web.config。


1

1)制作抽象Controller类。

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)在所有控制器中从此抽象类继承

public class HomeController : MyController
{}  

3)并在“视图共享”文件夹中添加一个名为“ NotFound”的视图。


0

我经历了此线程上发布的大多数解决方案。尽管这个问题可能很老,但即使在现在,它仍然非常适用于新项目,因此我花了很多时间来阅读此处以及其他地方给出的答案。

正如@Marco指出发生404的不同情况一样,我对照该列表检查了一起编译的解决方案。除了他的要求清单之外,我还添加了一个。

  • 该解决方案应该能够以最适当的方式处理MVC以及AJAX / WebAPI调用。(即,如果404发生在MVC中,则应显示“未找到”页面;如果404发生在WebAPI中,则不应劫持XML / JSON响应,以便使用中的Javascript可以轻松地对其进行解析)。

此解决方案是2折:

它的第一部分来自@Guillaume,网址https://stackoverflow.com/a/27354140/2310818。他们的解决方案可解决由于无效路由,无效控制器和无效操作引起的任何404错误。

这个想法是创建一个WebForm,然后使其调用MVC错误控制器的NotFound操作。它无需任何重定向即可完成所有这些操作,因此您不会在Fiddler中看到单个302。原始URL也被保留,这使该解决方案非常出色!


第二部分来自@Germán,网址https://stackoverflow.com/a/5536676/2310818。他们的解决方案可以处理您的操作以HttpNotFoundResult()形式返回的任何404或抛出新的HttpException()!

这个想法是让过滤器查看响应以及MVC控制器引发的异常,并在Errors Controller中调用适当的操作。同样,此解决方案无需任何重定向即可工作,并且保留了原始网址!


如您所见,这两个解决方案一起提供了非常强大的错误处理机制,它们满足了@Marco列出的所有要求以及我的要求。如果您想查看该解决方案的工作示例或演示,请在评论中留下,我很乐意将其组合在一起。


0

我浏览了所有文章,但对我没有用:我的要求用户在您的url自定义404页面中键入任何内容,我认为这很简单但是您应该正确理解404的处理方法:

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

我发现这篇文章非常有帮助。应该立即阅读。客户错误页面-Ben Foster

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.