Last active
February 9, 2018 21:33
-
-
Save Eonasdan/d724687e92de2390756f33adc64dfd6e to your computer and use it in GitHub Desktop.
Borrowed ExceptionHandlerMiddleware from Microsoft.AspNetCore.Diagnostics/Internal/DiagnosticsLoggerExtensions.cs that takes area into effect.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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