使用JSON的自定义404页面,无需重定向

时间:2018-08-29 03:20:45

标签: c# json asp.net-mvc api http-status-code-404

我正在开发API,到目前为止,除404页面(默认的ASP.Net 404页面)外,所有页面均返回JSON。我想更改它,以便它也返回404页面上的JSON。像这样:

{"Error":{"Code":1234,"Status":"Invalid Endpoint"}}

如果我在Global.asax.cs文件中发现404错误并重定向到现有路由,我会得到类似的效果:

// file: Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404)
    {
        Response.Redirect("/CatchAll");
    }
}

// file: HomeController.cs
[Route("CatchAll")]
public ActionResult UnknownAPIURL()
{
    return Content("{\"Error\":{\"Code\":1234,\"Status\":\"Invalid Endpoint\"}}", "application/json");
}

但是这会将302 HTTP代码返回到新URL,这不是我想要的。 我想返回正文中带有JSON的404 HTTP代码。我该怎么做?

我尝试过的方法,该方法不起作用...

#1-覆盖默认错误页面

我想也许我可以覆盖404处理程序中的默认错误页面。我这样尝试过:

// file: Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404)
    {
        Response.Clear();
        Response.StatusCode = 404;
        Response.TrySkipIisCustomErrors = true;
        Response.AddHeader("content-type", "application/json");
        Response.Write("{\"Error\":{\"Code\":1234,\"Status\":\"Invalid Endpoint\"}}");
    }
}

但是它只是继续提供默认的ASP错误页面。顺便说一句,我根本没有修改我的web.config文件。

#2-使用“包罗万象”路线

我尝试在路由表的末尾添加“全部捕获”路由。我的整个路由配置现在看起来像这样:

// file: RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapMvcAttributeRoutes();

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );

    routes.MapRoute(
        name: "NotFound",
        url: "{*data}",
        defaults: new { controller = "Home", action = "CatchAll", data = UrlParameter.Optional }
    );
}

但这也不起作用。如果我在Application_Error()内放置一个断点,我可以看到它仍然以404错误代码结尾。考虑到所有路线都应该匹配,我不确定这怎么可能?但是无论如何,它永远不会到达万能的路线。

#3-在运行时添加路由

我在另一个SO问题上看到了this answer,在这里可以从错误处理程序中调用新路由。所以我尝试了:

// file: Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Home");
        routeData.Values.Add("action", "CatchAll");

        Server.ClearError();
        Response.Clear();
        IController homeController = new HomeController();
        homeController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
}

但是会出现以下错误:

  

System.Web.Mvc.dll中发生了'System.Web.HttpException'类型的异常,但未在用户代码中处理

     

其他信息:在控制器Myproject.Controllers.HomeController上找不到公共操作方法'CatchAll'。

控制器肯定具有此方法。我正在使用POST调用API,但是我尝试通过在方法前添加[HttpPost]来专门制作用于发布的控制器,但仍然出现相同的错误。

2 个答案:

答案 0 :(得分:0)

在路由表的末尾有一条通吃的路由,看起来有点像

        config.Routes.MapHttpRoute(
            name: "NotImplemented",
            routeTemplate: "{*data}",
            defaults: new { controller = "Error", action = "notimplemented", data = UrlParameter.Optional });

我们在其中生成自定义响应。您也许可以在路由表中执行类似的操作。

答案 1 :(得分:0)

我终于找到了可行的解决方案。从上面看,它基本上是第一名,并增加了一行:

// file: Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpException && ((HttpException)ex).GetHttpCode() == 404)
    {
        Server.ClearError(); // important!!!
        Response.Clear();
        Response.StatusCode = 404;
        Response.TrySkipIisCustomErrors = true;
        Response.AddHeader("content-type", "application/json");
        Response.Write("{\"Error\":{\"Code\":1234,\"Status\":\"Invalid Endpoint\"}}");
    }
}

Server.ClearError();命令似乎非常重要。没有它,将返回常规的ASP错误页面,并且Response.方法都不会对返回的数据产生任何影响。