// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; using Microsoft.Framework.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Diagnostics { public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; private readonly ExceptionHandlerOptions _options; private readonly ILogger _logger; private readonly Func _clearCacheHeadersDelegate; public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ExceptionHandlerOptions options) { _next = next; _options = options; _logger = loggerFactory.CreateLogger(); if (_options.ExceptionHandler == null) { _options.ExceptionHandler = _next; } _clearCacheHeadersDelegate = ClearCacheHeaders; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError("An unhandled exception has occurred: " + ex.Message, ex); // We can't do anything if the response has already started, just abort. if (context.Response.HasStarted) { _logger.LogWarning("The response has already started, the error handler will not be executed."); throw; } PathString originalPath = context.Request.Path; if (_options.ExceptionHandlingPath.HasValue) { context.Request.Path = _options.ExceptionHandlingPath; } try { context.Response.Clear(); var exceptionHandlerFeature = new ExceptionHandlerFeature() { Error = ex, }; context.Features.Set(exceptionHandlerFeature); context.Response.StatusCode = 500; context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response); await _options.ExceptionHandler(context); // 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.LogError("An exception was thrown attempting to execute the error handler.", ex2); } finally { context.Request.Path = originalPath; } throw; // Re-throw the original if we couldn't handle it } } private 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.FromResult(0); } } }