Как обрабатывать исключения от 400 до 500 в ASP.NET Core 3 - PullRequest
1 голос
/ 12 октября 2019

У меня есть проект ASP.NET Core MVC (версия для Core - 3), и я должен обработать как минимум два исключения, таких как 404 и 500, и иметь представление для 404, которое должно сказать: «Извините, страница не найдена"и еще одна страница с ошибкой 500, на которой должно быть написано" Произошла ошибка при обработке вашего запроса ". Эти страницы должны иметь DefaultLayout. Мой файл Startup.cs выглядит следующим образом:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddDistributedMemoryCache();
        services.AddSession();
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        //services.AddScoped<Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration>();
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseStatusCodePages(async context =>
        {
            context.HttpContext.Response.ContentType = "text/plain";

            await context.HttpContext.Response.WriteAsync(
                "Status code page, status code: " +
                context.HttpContext.Response.StatusCode);
        });

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error/500");

            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.Use(async (ctx, next) =>
        {
            await next();

            if (ctx.Response.StatusCode == 404 && !ctx.Response.HasStarted)
            {
                //Re-execute the request so the user gets the error page
                string originalPath = ctx.Request.Path.Value;
                ctx.Items["originalPath"] = originalPath;
                ctx.Request.Path = "/error/404";
                await next();
            }
        });

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();
        app.UseSession();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });

    }
}

, и я создал контроллер ошибок, как показано ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace AgentRegister.Controllers
{
    [Route("error")]
    public class ErrorController : Controller
    {
        private readonly TelemetryClient _telemetryClient;

        public ErrorController(TelemetryClient telemetryClient)
        {
            _telemetryClient = telemetryClient;
        }
        [Route("500")]
        public IActionResult AppError()
        {
            var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            _telemetryClient.TrackException(exceptionHandlerPathFeature.Error);
            _telemetryClient.TrackEvent("Error.ServerError", new Dictionary<string, string>
            {
                ["originalPath"] = exceptionHandlerPathFeature.Path,
                ["error"] = exceptionHandlerPathFeature.Error.Message
            });
            return View();
        }


        [Route("404")]
        public IActionResult PageNotFound()
        {
            string originalPath = "unknown";
            if (HttpContext.Items.ContainsKey("originalPath"))
            {
                originalPath = HttpContext.Items["originalPath"] as string;
            }
            _telemetryClient.TrackEvent("Error.PageNotFound", new Dictionary<string, string>
            {
                ["originalPath"] = originalPath
            });
            return View();
        }

    }
}

Как я могу это сделать? Любая помощь будет оценена!

1 Ответ

2 голосов
/ 13 октября 2019

Подумайте об упрощении вашего контроллера, перенеся логику обработки ошибок в выделенный класс промежуточного программного обеспечения.

Представьте себе простой контроллер, подобный этому, все, что он делает, это определяет целевые страницы для маршрутизации и включает пример исключения для имитации ошибки 500. Это не заботится о конкретных типах ошибок. (Anon доступ разрешен для простоты.)

Проект называется TestError .

using System;    
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace TestError.Controllers
{
    [AllowAnonymous]
    public class HomeController : Controller
    {
        public HomeController() { }

        [HttpGet]
        public ViewResult Home() => View("Home");

        [HttpGet]
        public ViewResult Bogus() => throw new Exception("Bogus error");

        [HttpGet]
        public ViewResult Error() => View("Error");

        [HttpGet]
        public ViewResult PageNotFound() => View("PageNotFound");
    }
}

In Startup.cs , чуть вышеВ определениях маршрутов есть ссылка на обработчик ошибок: app.UseMiddleware<ErrorHandler>();

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TestError.Infrastructure;

namespace TestError
{
    public class Startup
    {
        //   Updated this class for ASP.NET Core 3
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        public void Configure(IApplicationBuilder app, IHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                 app.UseDeveloperExceptionPage();
            }
            app.UseStatusCodePages();
            app.UseStaticFiles();
            app.UseMiddleware<ErrorHandler>();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(name: "Default", 
                                                pattern: "{controller=Home}/{action=Home}/{id?}");

                endpoints.MapControllerRoute(name: "Error",
                                                "error",
                                                new { controller = "Home", action = "Error" });

                endpoints.MapControllerRoute(name: "PageNotFound",
                                                "pagenotfound",
                                                new { controller = "Home", action = "PageNotFound" });
            });
        }
    }
}

Startup.cs также требуется ссылка как using TestError.Infrastructure;, поскольку класс промежуточного программного обеспечения создается как Infrastructure \ ErrorHandler.cs

Класс промежуточного программного обеспечения просматривает конвейер HttpContext context.Response.StatusCode и использует оператор switch, вызывая пользовательский метод для ответа на каждую ошибку по мере необходимости, и я добавил предложениеза ошибку 404.

Вы можете добавлять дополнительные предложения для различных кодов ошибок по мере необходимости и добавлять пользовательские методы для работы с конкретными обстоятельствами или создавать их в отдельных классах, если вы хотите, чтобы обработчик ошибок не становился слишком сложным испецифичные.

Общие исключения кода обрабатываются отдельно как 500 ошибок, перехваченных блоком catch.

using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace TestError.Infrastructure
{
    public class ErrorHandler
    {
        private readonly RequestDelegate _next;

        public ErrorHandler(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);

                //  Handle specific HTTP status codes
                switch (context.Response.StatusCode)
                {
                    case 404:
                    HandlePageNotFound(context);
                    break;

                    case 418:
                    //  Not implemented
                    break;

                    default:
                    break;
                }
            }
            catch (Exception e)
            {
                //  Handle uncaught global exceptions (treat as 500 error)
                HandleException(context, e);
            }
            finally
            {
            }
        }

        //  500
        private static void HandleException(HttpContext context, Exception e)
        {
            context.Response.Redirect("/Error");
        }

        //  404
        private static void HandlePageNotFound(HttpContext context)
        {
            //  Display an information page that displays the bad url using a cookie
            string pageNotFound = context.Request.Path.ToString().TrimStart('/');
            CookieOptions cookieOptions = new CookieOptions();
            cookieOptions.Expires = DateTime.Now.AddMilliseconds(10000);
            cookieOptions.IsEssential = true;
            context.Response.Cookies.Append("PageNotFound", pageNotFound, cookieOptions);
            context.Response.Redirect("/PageNotFound");
        }
    }
}

Метод HandleException создает перенаправление на "/ Ошибка"page.

Error.cshtml в значительной степени соответствует ожиданиям

@{
    Layout = "~/Views/Shared/_Layout_Main.cshtml";
    ViewData["Title"] = "Error";
}
<div id="dataSection">
    <div class="TextLine Label">An error occurred while processing your request</div>
</div>

HandlePageNotFound создает недолговечный файл cookie для храненияадрес запрашиваемой страницы и перенаправляет на "/PageNotFound".

PageNotFound.cshtml ссылается на cookie для отображения значимой ошибки

@{
    Layout = "_Layout_Main";
    ViewData["Title"] = "Page Not Found";
    string notFoundMessage = "Sorry, The page cannot be found";
    string pageNotFound = Context.Request.Cookies["PageNotFound"];
    if (pageNotFound != null){
        notFoundMessage += " : ";
    }
}
<div id="dataSection">
    <div class="TextLine Label">@notFoundMessage<b>@pageNotFound</b></div>
</div>

Пример использования, тамдомашняя страница выглядит следующим образом (со страницей макета)

@{
    ViewData["Title"] = "Home";
    Layout = "~/Views/Shared/_Layout_Main.cshtml";
}

<h1>Hello World</h1>

Пример 1, / Home / Home : находки Home.cshtml enter image description here Пример 2, / Nopage : перенаправляет на PageNotFound.cshtml enter image description here Пример 3, / Home / Bogus : создает исключение и перенаправляет на Ошибка.cshtml enter image description here

Надеюсь, это поможет при обработке ошибок. Существует множество вариантов этого, и, как уже упоминалось, вы можете добавить больше предложений switch и даже включить конкретный регистр для 500.

Более подробный пример также будет включать в себя некоторые записи, которые я исключил для простоты. .

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