public async Task OnExceptionWritesAnErrorLogEntry() { var mockActionDescriptor = new Mock <HttpActionDescriptor>(); var mockLogger = new Mock <ILogger>(); var exception = new InvalidOperationException("One cannot simply walk into Mordor"); var errorConfiguration = new ErrorHandlingConfiguration(); var httpConfiguration = new HttpConfiguration(); var routeData = new HttpRouteData(new HttpRoute()); var request = new HttpRequestMessage(HttpMethod.Get, "http://api.someservice.com/collection/thing"); var controllerDescriptor = new HttpControllerDescriptor { Configuration = httpConfiguration, ControllerName = "generic" }; var controllerContext = new HttpControllerContext(httpConfiguration, routeData, request) { ControllerDescriptor = controllerDescriptor }; var actionContext = new HttpActionContext(controllerContext, mockActionDescriptor.Object); var errorContext = new HttpActionExecutedContext(actionContext, exception); mockLogger.Setup(logger => logger.ForContext(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>())).Returns(mockLogger.Object); mockActionDescriptor.SetupGet(descriptor => descriptor.ActionName).Returns("someAction"); request.SetConfiguration(httpConfiguration); request.SetRouteData(routeData); request.Headers.TryAddWithoutValidation(HttpHeaders.CorrelationId, "Not-A-Real-Value"); var exceptionFilter = new GlobalExceptionFilter(errorConfiguration, mockLogger.Object); await exceptionFilter.OnExceptionAsync(errorContext, CancellationToken.None); mockLogger.Verify(logger => logger.Error(It.Is <Exception>(ex => ex == exception), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>()), Times.Once, "An error log entry should have been written"); }
/// <summary> /// Initializes a new instance of the <see cref="GlobalExceptionFilter"/> class. /// </summary> /// /// <param name="configuration">The configuration to use for error handling decisions.</param> /// <param name="log">The log instance to be used for capturing information around observed exceptions.</param> /// public GlobalExceptionFilter(ErrorHandlingConfiguration configuration, ILogger log) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (log == null) { throw new ArgumentNullException(nameof(log)); } this.Configuration = configuration; this.Log = log; }
public async Task OnExceptionSetsTheResultWithExceptionContentWhenEnabled() { var mockActionDescriptor = new Mock <HttpActionDescriptor>(); var mockLogger = new Mock <ILogger>(); var exception = new InvalidOperationException("One cannot simply walk into Mordor"); var errorConfiguration = new ErrorHandlingConfiguration { ExceptionDetailsEnabled = true }; var httpConfiguration = new HttpConfiguration(); var routeData = new HttpRouteData(new HttpRoute()); var request = new HttpRequestMessage(HttpMethod.Get, "http://api.someservice.com/collection/thing"); var controllerDescriptor = new HttpControllerDescriptor { Configuration = httpConfiguration, ControllerName = "generic" }; var controllerContext = new HttpControllerContext(httpConfiguration, routeData, request) { ControllerDescriptor = controllerDescriptor }; var actionContext = new HttpActionContext(controllerContext, mockActionDescriptor.Object); var errorContext = new HttpActionExecutedContext(actionContext, exception); mockLogger.Setup(logger => logger.ForContext(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>())).Returns(mockLogger.Object); mockActionDescriptor.SetupGet(descriptor => descriptor.ActionName).Returns("someAction"); request.SetConfiguration(httpConfiguration); request.SetRouteData(routeData); request.Headers.TryAddWithoutValidation(HttpHeaders.CorrelationId, "Not-A-Real-Value"); var exceptionFilter = new GlobalExceptionFilter(errorConfiguration, mockLogger.Object); await exceptionFilter.OnExceptionAsync(errorContext, CancellationToken.None); errorContext.Response.Should().NotBeNull("because the response should have been set"); errorContext.Response.StatusCode.Should().Be(HttpStatusCode.InternalServerError, "because an exception result should be set"); errorContext.Response.ReasonPhrase.Should().NotContain(exception.ToString(), "because exception details should not appear as the reason"); errorContext.Response.Content.Should().NotBeNull("because body content should have been set"); var exceptionContent = await errorContext.Response.Content.ReadAsAsync <Exception>(); exceptionContent.Message.Should().Be(exception.Message, "because the exception should have been used as the content"); exceptionContent.StackTrace.Should().Be(exception.StackTrace, "because the exception should have been used as the content"); }
public async Task ExceptionHeaderDoesNotContainUnsafeCharacters() { var mockActionDescriptor = new Mock <HttpActionDescriptor>(); var mockLogger = new Mock <ILogger>(); var exception = new InvalidOperationException("One cannot simply walk into Mordor"); var errorConfiguration = new ErrorHandlingConfiguration { ExceptionDetailsEnabled = true }; var httpConfiguration = new HttpConfiguration(); var routeData = new HttpRouteData(new HttpRoute()); var request = new HttpRequestMessage(HttpMethod.Get, "http://api.someservice.com/collection/thing"); var controllerDescriptor = new HttpControllerDescriptor { Configuration = httpConfiguration, ControllerName = "generic" }; var controllerContext = new HttpControllerContext(httpConfiguration, routeData, request) { ControllerDescriptor = controllerDescriptor }; var actionContext = new HttpActionContext(controllerContext, mockActionDescriptor.Object); var errorContext = new HttpActionExecutedContext(actionContext, exception); mockLogger.Setup(logger => logger.ForContext(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>())).Returns(mockLogger.Object); mockActionDescriptor.SetupGet(descriptor => descriptor.ActionName).Returns("someAction"); request.SetConfiguration(httpConfiguration); request.SetRouteData(routeData); request.Headers.TryAddWithoutValidation(HttpHeaders.CorrelationId, "Not-A-Real-Value"); var exceptionFilter = new GlobalExceptionFilter(errorConfiguration, mockLogger.Object); await exceptionFilter.OnExceptionAsync(errorContext, CancellationToken.None); errorContext.Response.Should().NotBeNull("because the response should have been set"); IEnumerable <string> headers; errorContext.Response.Headers.TryGetValues(HttpHeaders.ExceptionDetails, out headers).Should().BeTrue("because the exception details header should have been set"); headers.Count().Should().Be(1, "because a single exception detail header should exist"); headers.First().Should().NotContain(Environment.NewLine, "because new lines are not allowed in header values"); }