Instantly share code, notes, and snippets.

Embed
What would you like to do?
Borrowed ExceptionHandlerMiddleware from Microsoft.AspNetCore.Diagnostics/Internal/DiagnosticsLoggerExtensions.cs that takes area into effect.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Akron.Web.Core.Middleware
{
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ExceptionHandlerOptionsExtra _options;
private readonly ILogger _logger;
private readonly Func<object, Task> _clearCacheHeadersDelegate;
private readonly DiagnosticSource _diagnosticSource;
public ExceptionHandlerMiddleware(
RequestDelegate next,
ILoggerFactory loggerFactory,
IOptions<ExceptionHandlerOptionsExtra> options,
DiagnosticSource diagnosticSource)
{
_next = next;
_options = options.Value;
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
_clearCacheHeadersDelegate = ClearCacheHeaders;
_diagnosticSource = diagnosticSource;
if (_options.ExceptionHandler != null) return;
_options.ExceptionHandler = _next;
}
[UsedImplicitly]
public async Task Invoke(HttpContext context)
{
var area = "";
if (_options.AppendAreaToHandler)
{
if (context.Request.Path.Value.StartsWith("/App")) area = "/App";
if (context.Request.Path.Value.StartsWith("/api")) area = "api";
if (context.Request.Path.Value.StartsWith("/signin")) area = "signin";// "/signin" comes from external login callback
}
var originalPath = context.Request.Path;
try
{
await _next(context);
if (area != "api" && area != "signin")
{
// "/signin" comes from external login callback
if (context.Response.StatusCode == 404 && !context.Response.HasStarted)
{
context.Items["originalPath"] = originalPath;
context.Request.Path = $"{area}/{_options.ErrorController}/{_options.NotFoundAction}";
await _next(context);
}
}
}
catch (Exception ex)
{
//_logger.UnhandledException(ex);
// We can't do anything if the response has already started, just abort.
if (context.Response.HasStarted)
{
//_logger.ResponseStartedErrorHandler();
throw;
}
context.Request.Path = $"{area}/{_options.ErrorController}/{_options.ErrorAction}";
try
{
context.Response.Clear();
var exceptionHandlerFeature = new ExceptionHandlerFeature
{
Error = ex,
Path = originalPath.Value
};
context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);
context.Response.StatusCode = 500;
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
await _options.ExceptionHandler(context);
if (_diagnosticSource.IsEnabled("Microsoft.AspNetCore.Diagnostics.HandledException"))
{
_diagnosticSource.Write("Microsoft.AspNetCore.Diagnostics.HandledException", new { httpContext = context, exception = ex });
}
// TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
return;
}
catch (Exception ex2)
{
// Suppress secondary exceptions, re-throw the original.
// _logger.ErrorHandlerException(ex2);
}
finally
{
context.Request.Path = originalPath;
}
throw; // Re-throw the original if we couldn't handle it
}
}
private static Task ClearCacheHeaders(object state)
{
var response = (HttpResponse)state;
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
return Task.CompletedTask;
}
}
public class ExceptionHandlerOptionsExtra : ExceptionHandlerOptions
{
public bool AppendAreaToHandler { get; set; } = true;
public string ErrorController { get; set; } = "Home";
public string NotFoundAction { get; set; } = "NotFound";
public string ErrorAction { get; set; } = "Error";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment