Я разрабатываю API, и пока все страницы возвращают JSON, за исключением страницы 404, которая является страницей ASP.Net 404 по умолчанию. Я хотел бы изменить это так, чтобы он возвращал JSON и на странице 404. Примерно так:
{"Error":{"Code":1234,"Status":"Invalid Endpoint"}}
Я могу получить аналогичный эффект, если поймать 404 ошибки в файле Global.asax.cs и перенаправить на существующий маршрут:
// 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");
}
Но это возвращает HTTP-код 302 на новый URL-адрес, а это не то, что мне нужно. Я хочу вернуть HTTP-код 404 с JSON в теле. Как я могу это сделать?
Вещи, которые я пробовал, которые не работали ...
# 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, где можно вызвать новый маршрут из обработчика ошибок. Итак, я попробовал это:
// 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.HttpException' произошло в System.Web.Mvc.dll, но не было обработано в коде пользователя
Дополнительная информация: Метод публичного действия 'CatchAll' не найден на контроллере Myproject.Controllers.HomeController
.
Контроллер определенно имеет этот метод. Я вызываю API с помощью POST, однако я попытался создать контроллер специально для публикации, добавив [HttpPost]
перед методом, и я все еще получаю ту же ошибку.