我用的是RC2

使用URL路由:

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。配置customErrors作为我的404处理程序,忘记这一切?我假设如果我使用customErrors,由于Web的原因,我将不得不在/Views之外存储通用404页面。配置直接访问限制。


当前回答

我能让@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。

其他回答

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”的视图。

我真的很喜欢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两次触发异常例程。Eg的url,如asdfsdf/dfgdfgd

我能让@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。

我已经调查了很多关于如何正确地管理MVC中的404(特别是MVC3),这是我提出的最好的解决方案:

在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,有6种不同的情况,ASP。NET MVC3应用程序可以生成404。

(由ASP自动生成。净框架:)

(1)路由表中没有匹配的URL。

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

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

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

(手动生成:)

一个动作通过使用HttpNotFound()方法返回一个HttpNotFoundResult。

动作抛出一个状态代码为404的HttpException。

(6)动作手动修改响应。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)、(4)、(6)中不符合目标(1)。 不自动符合目标(2)。它必须手动编程。 不符合目标(3)。

(2)

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

此解决方案存在的问题:

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

(3)

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

此解决方案存在的问题:

仅适用于iis7 +。 不自动符合目标(2)。它必须手动编程。 它模糊了应用程序级别的http异常。例如,不能使用customErrors部分,System.Web.Mvc。HandleErrorAttribute等等。它不能只显示一般的错误页面。

(4)

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

and

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

此解决方案存在的问题:

仅适用于iis7 +。 不自动符合目标(2)。它必须手动编程。 在情况(2)、(3)、(5)中不符合目标(3)。

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

处理ASP中的错误。NET MVC就是个麻烦。我在这个页面和其他问题和网站上尝试了很多建议,都没有什么效果。一个建议是处理网络上的错误。系统内部配置。web服务器,但只返回空白页。

当我想出这个解决方案时,我的目标是;

不重定向 返回正确的STATUS CODES,而不是像默认的错误处理那样返回200/Ok

这是我的解决方案。

1.将以下内容添加到系统中。网络部分

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

上面的处理没有路由处理的任何url。配置和未处理的异常,特别是在视图上遇到的异常。注意我用的是aspx而不是html。这样我就可以在后面的代码上添加响应代码。

2. 在项目的根目录下创建一个名为Error的文件夹(或者其他你喜欢的文件夹),并添加这两个webform。下面是我的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级别的错误。