Пользовательская страница 404 с JSON без перенаправления - PullRequest
0 голосов
/ 29 августа 2018

Я разрабатываю 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] перед методом, и я все еще получаю ту же ошибку.

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Я наконец нашел решение, которое работает. Это в основном # 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. не оказывает никакого влияния на возвращаемые данные.

0 голосов
/ 29 августа 2018

В конце таблицы маршрутов у нас есть универсальный маршрут, который немного похож на

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

где мы генерируем пользовательский ответ. Возможно, вы сможете сделать что-то подобное в своей таблице маршрутов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...