带有 JSON 且无重定向的自定义 404 页面
Custom 404 page with JSON without redirect
我正在开发一个 API 和目前所有的页面 return JSON 除了 404 页面,它是默认的 ASP.Net 404 页面。我想更改它,以便它也在 404 页面上 returns 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");
}
但这 return 是新 URL 的 302 HTTP 代码,这不是我想要的。 我想 return 正文中带有 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 - 使用 "catch-all" 路线
我尝试在路线 table 的末尾添加一条 "catch-all" 路线。我的整个路线配置现在看起来像这样:
// 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 错误代码结束。我不确定这怎么可能,因为应该匹配 catch all 路由?但无论如何它永远不会到达包罗万象的路线。
#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));
}
}
但是它给出了以下错误:
An exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: A public action method 'CatchAll' was not found on controller Myproject.Controllers.HomeController
.
控制器肯定有这个方法。我用 POST 调用 API,但是我尝试通过在方法之前添加 [HttpPost]
来专门为 post 制作控制器,但我仍然遇到相同的错误。
我们在路线 table 的尽头有一条包罗万象的路线,看起来有点像
config.Routes.MapHttpRoute(
name: "NotImplemented",
routeTemplate: "{*data}",
defaults: new { controller = "Error", action = "notimplemented", data = UrlParameter.Optional });
我们在其中生成自定义响应。您也许可以在路线 table.
中做类似的事情
我终于找到了有效的解决方案。它基本上是上面的#1,添加了额外的一行:
// 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.
方法的 none 会对返回的数据产生任何影响。
我正在开发一个 API 和目前所有的页面 return JSON 除了 404 页面,它是默认的 ASP.Net 404 页面。我想更改它,以便它也在 404 页面上 returns 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");
}
但这 return 是新 URL 的 302 HTTP 代码,这不是我想要的。 我想 return 正文中带有 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 - 使用 "catch-all" 路线
我尝试在路线 table 的末尾添加一条 "catch-all" 路线。我的整个路线配置现在看起来像这样:
// 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 错误代码结束。我不确定这怎么可能,因为应该匹配 catch all 路由?但无论如何它永远不会到达包罗万象的路线。
#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));
}
}
但是它给出了以下错误:
An exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: A public action method 'CatchAll' was not found on controller
Myproject.Controllers.HomeController
.
控制器肯定有这个方法。我用 POST 调用 API,但是我尝试通过在方法之前添加 [HttpPost]
来专门为 post 制作控制器,但我仍然遇到相同的错误。
我们在路线 table 的尽头有一条包罗万象的路线,看起来有点像
config.Routes.MapHttpRoute(
name: "NotImplemented",
routeTemplate: "{*data}",
defaults: new { controller = "Error", action = "notimplemented", data = UrlParameter.Optional });
我们在其中生成自定义响应。您也许可以在路线 table.
中做类似的事情我终于找到了有效的解决方案。它基本上是上面的#1,添加了额外的一行:
// 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.
方法的 none 会对返回的数据产生任何影响。