ASP.NET中的MVC如何使用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET中的MVC如何使用?相关的知识,希望对你有一定的参考价值。

上面的这位同学
在回答什么
我都没明白,他都没说报错
你就知道是单元测试报错?
开玩笑
这样的一概而论
还说自己非常全面的研究了MVC?

ASP.NET
在创建项目的时候
可以创建一个MVC得框架
然后会提示
是否创建测试
选择否
然后创建一个MVC
。然后你可以运行一下
看下效果。在URL
路由方面
Controllers->HomeControllers.cs->创建新方法返回ViewResult()->Views->Home->创建新页面->Shared->site.Master->添加《%:html.ActionLink("方法名称","页面名称","Home")%》
-》运行。

一般在HomeCollers.cs里面创建的方法名和
Views
文件夹得Home下面
创建的页面的名字是相同的。
参考技术A MVC完全分离了代码和视图层
C是控制
V是视图
M是模型
这个3个层是完全找不到其他层的代码的希望你想用MVC的话那么你就要遵守那的使用规则不要代码混排
WebFrom
可以允许不过你在MCV这样写那么你就打破了MVC的最初的设计理念MVC更像是一个开发框架人家给你做了路由
做了引擎你去用就行了
参考技术B 那是你的单元测试项目报的错,一般情况下如果你做的是富客户端,根本没法用单元测试,因为数据传送的时候用的都是json,xml等,在路由器基本不传什么参数,所以建项目一般不要单元测试。如果想学mvc找我就行了,在mvc
Entity
FrameWork
jquery
Linq方面有比较全面的研究

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

【中文标题】如何正确处理 ASP.NET MVC 中的 404?【英文标题】:How can I properly handle 404 in ASP.NET MVC? 【发布时间】:2010-10-11 19:37:52 【问题描述】:

我正在使用 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);
  

但是,之前的策略不处理对 Bad/Unknown 控制器的请求。例如,我没有“/IDoNotExist”,如果我请求这个,我会从 Web 服务器获得通用 404 页面,如果我使用路由 + 覆盖,则不是我的 404。

最后,我的问题是:有没有什么方法可以在 MVC 框架本身中使用路由或其他东西来捕获这种类型的请求?

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

【问题讨论】:

这是 404 错误,我不会在意它。让它显示 404。因为肯定是用户输入了错误的东西。或者如果它是移动的东西,那么您的应用程序应该接受该请求并永久重定向。 404 属于网络服务器而不是应用程序。您可以随时自定义 iis 页面以防出错。 你也可以看看这个解决方案blog.dantup.com/2009/04/… ben.onfabrik.com/posts/aspnet-mvc-custom-error-pages也有一些不错的资料 遗憾的是,4 个稳定版本在 5 年多之后,在 asp.net MVC + IIS 中处理 404 的情况并没有真正改善,这仍然是关于如何进行的问答处理它。 【参考方案1】:

代码取自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));

【讨论】:

更新:肯定需要检查 http 404,但我仍然不太确定你什么时候会得到 500。你还需要明确设置 Response.StatusCode = 404 或500 否则,如果您返回 200 状态代码(此代码当前执行的状态代码),谷歌将开始为这些页面编制索引 @Simon_Weaver:同意!这需要返回 404 状态码。在它出现之前,它基本上作为 404 解决方案被破坏了。检查这个:codinghorror.com/blog/2007/03/… 整个建议有一个潜在的缺陷——当执行到 Global.asax 时,太多的 HttpContext 丢失了。您不能像示例建议的那样路由回您的控制器。请参阅顶部博客链接中的 cmets。 正如上面的一些 cmets 和链接的帖子所提到的,这似乎不起作用。错误控制器被命中,但返回一个空白屏幕。 (使用 mvc 3) 感觉不对劲,MVC 的全部目的是删除所有抽象,但它又来了……【参考方案2】:

404 的要求

以下是我对 404 解决方案的要求,下面我将展示我如何实现它:

我想处理带有错误操作的匹配路由 我想用坏控制器处理匹配的路由 我想处理不匹配的路由(我的应用程序无法理解的任意 url)——我不希望这些冒泡到 Global.asax 或 IIS,因为那时i can't redirect back into my MVC app properly 我想要一种以与上述相同的方式处理自定义 404 的方法 - 比如为不存在的对象(可能已删除)提交 ID 时 我希望我的所有 404 都返回一个 MVC 视图(不是静态页面),如果需要,我可以稍后将更多数据发送到该视图(good 404 designs)并且他们必须 返回 HTTP 404 状态码

解决方案

我认为您应该将 Application_Error 保存在 Global.asax 中以用于更高级别的内容,例如未处理的异常和日志记录(例如 Shay Jacoby's answer 显示),而不是 404 处理。这就是为什么我的建议将 404 内容保留在 Global.asax 文件之外的原因。

第 1 步:为 404 错误逻辑有一个共同的地方

这是一个可维护性的好主意。使用ErrorController 以便将来对well designed 404 page 的改进可以轻松适应。另外,确保您的回复包含 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 操作创建了一个公共位置。想想DRY!

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 步:在控制器工厂中使用依赖注入并连接 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 步:将 NotFound 路由添加到 Global.asax,以获取无法解析到您的应用中的网址

这条路线应该指向我们的Http404 动作。注意 url 参数将是一个相对 url,因为路由引擎在这里剥离了域部分?这就是为什么我们在步骤 1 中有所有条件 url 逻辑。

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

这是在您不自己调用的 MVC 应用程序中捕获 404 的第三个也是最后一个位置。如果您没有在此处捕获不匹配的路由,那么 MVC 会将问题传递给 ASP.NET (Global.asax),而在这种情况下您并不真正想要这样。

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

就像将错误 ID 提交给我的 Loans 控制器(源自 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);
    

如果所有这些都可以用更少的代码连接到更少的地方,那就太好了,但我认为这个解决方案更易于维护、更可测试且相当实用。

感谢您迄今为止的反馈。我很想得到更多。

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

【讨论】:

感谢您的完整报道。一个补充是,在 IIS7 下运行时,您需要将属性“TrySkipIisCustomErrors”设置为 true。否则 IIS 仍将返回默认的 404 页面。我们添加了 Response.TrySkipIiisCustomErrors=true;在步骤 5 中设置状态代码的行之后。 msdn.microsoft.com/en-us/library/… @Ryan web.config 的customErrors 部分定义了静态重定向页面,如果不是 IIS,这些页面将在 aspnet 中进行高级处理。这不是我想要的,因为我需要结果是 MVC 视图(所以我可以在其中包含数据等)。我不会断然说“customErrors 在 MVC 中已过时”,但对我和这个 404 解决方案来说,它们肯定是。 另外,有人可以更新第 3 步以便不使用 StructureMap 吗?如果您还没有使用 ControllerFactory,也许只是一个通用的 ControllerFactory,它很容易实现。 这适用于 MVC3。我将 ObjectFactory.GetInstance 切换为 MVC3 的 DependencyResolver.Current.GetService,所以它更通用。我正在使用 Ninject。 有没有其他人觉得像 404 这样常见的事情在网络框架中如此复杂如此血腥复杂。【参考方案3】:

ASP.NET MVC 不能很好地支持自定义 404 页面。自定义控制器工厂、包罗万象的路由、带有HandleUnknownAction 的基本控制器类 - 啊!

到目前为止,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();
    

示例项目

Test404 on GitHub Live website

【讨论】:

这应该是公认的答案!!!在带有 IIS Express 的 ASP.NET MVC 3 上表现出色。 如果您使用的是 IIS7+,这绝对是正确的选择。 +1! 当您在同一个项目中使用 JSON 时,是否可以只返回状态 404? 这在 iis express 中运行良好,但是一旦我在生产 IIS 7.5 中部署该站点,我得到的只是一个白页而不是错误视图。 根据我的测试(使用 MVC3),这打破了 customErrors mode="On"HandleErrorAttribute 的功能。不再提供控制器操作中未处理异常的自定义错误页面。【参考方案4】:

快速回答/TL;DR

对于那些懒惰的人:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

然后从global.asax中删除这一行

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

这仅适用于 IIS7+ 和 IIS Express。

如果您使用的是 Cassini .. 好吧 .. 嗯 .. 呃 .. 尴尬 ...


冗长的解释性答案

我知道这已得到答复。但答案真的很简单(感谢 David Fowler 和 Damian 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>

现在请仔细记下我决定使用的 ROUTES。你可以使用任何东西,但我的路线是

/NotFound /ServerError

看看&lt;system.web&gt; 的第一部分如何只有一个自定义条目? statusCode="404" 条目?我只列出了一个状态代码,因为所有其他错误,包括500 Server Error(即当您的代码有错误并使用户请求崩溃时发生的那些讨厌的错误)..所有其他错误都由设置处理@ 987654340@..里面说,如果你不是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 (哦!奖励忽略路线,给你!) 然后(这里的命令是必要的),我有两个明确的错误处理路由.. 后面跟着任何其他路由。在这种情况下,默认一个。当然,我还有更多,但这对我的网站来说是特别的。 只需确保错误路由位于列表顶部即可。秩序是必要的

最后,当我们在 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");
    

好的,让我们看看这个。首先,这里有 NO [HandleError] 属性。为什么?因为内置的ASP.NET 框架已经在处理错误并且我们已经指定了处理错误需要做的所有事情:) 它在这个方法中!

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

Bonus WTF:是的,我做了第三种操作方法,用于测试错误处理。

第 4 步 - 创建视图

最后,创建两个视图。将 em 放在这个控制器的正常视图位置。

奖金奖励

您不需要Application_Error(object sender, EventArgs e) 上述步骤都可以 100% 完美地与Elmah 配合使用。 Elmah fraking wroxs!

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

现在,恭喜您阅读了这么多内容并获得了一只独角兽作为奖品!

【讨论】:

所以我尝试实现这一点,但有几个问题......首先,您需要在 weeb.config 中的路径之前添加一个 ~ ,否则它不适用于虚拟目录。 2-如果 IIS 自定义错误触发并且视图正在使用它根本不呈现的布局,只是一个白页。我通过将此行添加到控制器“Response.TrySkipIisCustomErrors = true;”中解决了这个问题.但是,如果您转到一个文件但 404 的 url,它仍然不起作用。像 mysite/whatever/fake.html 得到一个白页。 -1,对不起,对我来说,任何改变 404 url​​ 的解决方案都是错误的。并且使用 webconfig 在 MVC 中无法在不更改 url 的情况下处理它,或者您需要创建静态 html 文件或 aspx(是的,普通的旧 aspx 文件)才能做到这一点。如果您喜欢 ?aspxerrorpath=/er/not/found 在网址中包含,您的解决方案很好。 这听起来可能很奇怪 - 但我的答案是很久以前提供的,我同意你的@Gutek,我不喜欢重定向到错误页面不再。我曾经(参考我的回答:P)。如果错误发生在 /some/resource .. 则该资源应返回 404 或 500 等。否则会产生巨大的 SEO 影响。啊..时代如何变化:) @Gutek 你知道 customErrors redirectMode="ResponseRewrite" 吗?从安全角度来看,返回 404 并不理想 @Chris 该死的。我什至不记得现在是什么了。好吧,我的 meme 收藏来救援了……并且……修复了。【参考方案5】:

我研究了 A LOT 关于如何正确管理 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));
        
    

错误控制器:

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;
    

(可选)

说明:

据我所知,ASP.NET MVC3 应用程序可以在 6 种不同的情况下生成 404。

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

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

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

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

这个解决方案的问题:

    仅适用于 IIS 7+。 在情况 (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>

这个解决方案的问题:

    仅适用于 IIS 7+。 不自动符合目标 (2)。必须手动编程。 它掩盖了应用程序级别的 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>

这个解决方案的问题:

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

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

【讨论】:

很好的答案。值得更多的支持。为什么您的 global.asax 代码在 Application_Error 中不起作用/不属于。 谢谢!它不能在 Application_Error 下完成,因为从控制器抛出的显式 404 在 ASP.NET 上不被视为错误。如果您从控制器返回 HttpNotFound(),则 Application_Error 事件将永远不会触发。 我认为您在 ErrorsController 中忘记了 public ActionResult NotFound() 。另外,您能否解释一下您的 _NotFound 部分对于 AJAX 请求的外观? 使用 MVC 4 我总是得到MissingMethodException: Cannot create an abstract class 在线c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); 有什么想法吗? 如果“未找到” URL 在路径中包含一个点(例如 example.com/hi.bob),那么 Application_EndRequest 根本不会触发,我会得到 IE 的通用 404 页面。【参考方案6】:

我真的很喜欢 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 之类的 url

【讨论】:

这太棒了。那些“两次”案件开始困扰我。 更新了我的答案 如果用户输入错误的控制器和动作名称,上述解决方案是否有效?【参考方案7】:

我可以让@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?) 我认为关键是修改现有请求而不是创建新请求,但我前一阵子这样做了,所以我不确定是不是这样。 “InvokeHttp404”在控制器工厂不起作用。 我今天用一些 MVC2 细节更新了我的答案。你能告诉我我上面详述的解决方案是否仍然不适合你吗?【参考方案8】:

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

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

此解决方案还使用有利于 MVC 3 错误过滤器的最小 web.config 设置。

用法

只需从动作或自定义 ActionFilterAttribute 中抛出 HttpException。

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

步骤 1

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

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

第二步

添加类似于MVC框架的HandleErrorAttribute的自定义HandleHttpErrorAttribute,HTTP错误除外:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp0"

    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

第三步

将过滤器添加到 Global.asax 中的 GlobalFilterCollection (GlobalFilters.Filters)。此示例将所有 InternalServerError (500) 错误路由到错误共享视图 (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 的帖子和这些文章都是很好的参考。

Problems using CustomErrors redirectMode=ResponseRewrite Elmah + MVC HandleErrorAttribute

【讨论】:

嗯,我无法让它工作:The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'. - 知道为什么我会这样做吗? redirectMode="ResponseRedirect"。这将返回 302 Found + 200 OK,这对 SEO 不利!【参考方案9】:

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

    创建视图 404.cshtml。

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

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

    创建一个自定义控制器工厂,返回您的基本控制器作为后备:

    public class ControllerFactory : DefaultControllerFactory
    
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        
    
    

    添加到Application_Start()下面一行:

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

【讨论】:

【参考方案10】:

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

课程API控制器

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

结果

【讨论】:

【参考方案11】:

处理 ASP.NET MVC 中的错误只是一件麻烦事。我在此页面以及其他问题和网站上尝试了很多建议,但没有任何效果。一个建议是在 system.webserver 内处理 web.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>

上面处理了 routes.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 添加到您的 Shared 视图文件夹中。下面是我的;

<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 级别错误。

【讨论】:

【参考方案12】:

在 nuget 上尝试 NotFoundMVC。它可以工作,无需设置。

【讨论】:

http://localhost/Views/Shared/NotFound.cshtml 不会生成自定义 404 页面。 很容易定制。您可以访问请求的 url 和引荐来源网址,因此您可以做自己喜欢的事情。我用这个包,效果很好。 这是一个很棒的包,前提是您不会使用 async Task 动作(或其他类似的 async )动作。在 MVC 5 上,这是一个破碎的场景。 GitHub 上有一个 fork 可以规避这一点,但对我来说,这是不行的。【参考方案13】:

我的解决方案,以防有人发现它有用。

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

Shared 文件夹中添加PageNotFound.cshtml,就是这样。

【讨论】:

这不会向客户端发出 302 重定向然后 200(OK)状态吗?他们不应该仍然获得 404 状态吗? @Konamiman 你确定你的代码中的行应该是model.RequestedUrl = Request.Url.OriginalString.Contains(url) &amp; Request.Url.OriginalString != url ? Request.Url.OriginalString : url;而不是model.RequestedUrl = Request.Url.OriginalString.Contains(url) &amp;&amp; Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(&而不是&&)?【参考方案14】:

在我看来,标准的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 开发服务器,Cassini) 经典模式下的 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() 

        


    


用法

将此作为最终的 HTTP 模块包含在您的 web.config 中

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


【讨论】:

【参考方案15】:

由于我的评论太长,所以发布答案...

这是对独角兽帖子/答案的评论和问题:

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

我更喜欢这个答案而不是其他答案,因为它很简单,而且显然咨询了微软的一些人。但是我收到了三个问题,如果可以回答,那么我将把这个答案称为 ASP.NET MVC (x) 应用程序的互联网上所有 404/500 错误答案的圣杯。

@Pure.Krome

    您能否使用 GWB 指出的 cmets 中的 SEO 内容更新您的答案(您的答案中从未提及这一点) - &lt;customErrors mode="On" redirectMode="ResponseRewrite"&gt;&lt;httpErrors errorMode="Custom" existingResponse="Replace"&gt;

    您能否询问您的 ASP.NET 团队朋友是否可以这样做 - 很高兴得到一些确认 - 更改 redirectModeexistingResponse 可能是一个很大的禁忌这种方式可以很好地与 SEO 一起玩?!

    在与您在 Microsoft 的朋友交谈后,您能否对所有这些内容(customErrors redirectMode="ResponseRewrite"customErrors redirectMode="ResponseRedirect"httpErrors errorMode="Custom" existingResponse="Replace"、REMOVE customErrors 完全按照某人的建议进行说明)?

正如我所说;如果我们能让您的答案更完整,那就太好了,因为这似乎是一个相当受欢迎的问题,有 54 000 多次浏览。

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

【讨论】:

【参考方案16】:

添加我的解决方案,该解决方案与 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。

【讨论】:

【参考方案17】:

1) 制作抽象控制器类。

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) 并在您的 View-Shared 文件夹中添加一个名为“NotFound”的视图。

【讨论】:

【参考方案18】:

我浏览了此线程上发布的大多数解决方案。虽然这个问题可能很老,但即使现在它仍然非常适用于新项目,所以我花了很多时间阅读这里以及其他地方提供的答案。

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

该解决方案应该能够以最合适的方式处理 MVC 以及 AJAX/WebAPI 调用。 (即,如果 404 发生在 MVC 中,它应该显示 Not Found 页面,如果 404 发生在 WebAPI 中,它不应该劫持 XML/JSON 响应,以便使用 Javascript 可以轻松解析它)。

这个解决方案是2折:

第一部分来自@Guillaume https://***.com/a/27354140/2310818。他们的解决方案负责处理由于无效路由、无效控制器和无效操作而导致的任何 404。

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


第二部分来自@Germánhttps://***.com/a/5536676/2310818。他们的解决方案以 HttpNotFoundResult() 或 throw new HttpException() 的形式处理您的操作返回的任何 404!

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


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

【讨论】:

【参考方案19】:

我浏览了所有文章,但没有什么对我有用: 我要求用户在您的 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>

我觉得这篇文章很有帮助。应该立即阅读。Custome error page-Ben Foster

【讨论】:

以上是关于ASP.NET中的MVC如何使用?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何在asp.net mvc 5身份中的授权属性中使用动态角色

如何允许 IIS 使用 ASP.NET MVC 项目中的本地数据库?

如何使用 DataAnnotations 处理 ASP.NET MVC 2 中的布尔值/复选框?

如何使用 ASP.Net MVC 路由来路由图像?

如何使用 ASP.NET MVC 5 中的 JavaScript 在运行时使用路由值增加 actionlink?