diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
index 22c7c39b47..c712d80362 100644
--- a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.Internal;
@@ -17,11 +18,12 @@ using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.StackTrace.Sources;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Diagnostics
{
///
- /// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses.
+ /// Captures synchronous and asynchronous exceptions from the pipeline and generates error responses.
///
public class DeveloperExceptionPageMiddleware
{
@@ -32,6 +34,7 @@ namespace Microsoft.AspNetCore.Diagnostics
private readonly DiagnosticSource _diagnosticSource;
private readonly ExceptionDetailsProvider _exceptionDetailsProvider;
private readonly Func _exceptionHandler;
+ private static readonly MediaTypeHeaderValue _textPlainMediaType = new MediaTypeHeaderValue("text/html");
///
/// Initializes a new instance of the class
@@ -127,13 +130,34 @@ namespace Microsoft.AspNetCore.Diagnostics
// Assumes the response headers have not been sent. If they have, still attempt to write to the body.
private Task DisplayException(ErrorContext errorContext)
{
- var compilationException = errorContext.Exception as ICompilationException;
- if (compilationException != null)
+ var httpContext = errorContext.HttpContext;
+ var headers = httpContext.Request.GetTypedHeaders();
+ var acceptHeader = headers.Accept;
+
+ // If the client does not ask for HTML just format the exception as plain text
+ if (acceptHeader == null || !acceptHeader.Any(h => h.IsSubsetOf(_textPlainMediaType)))
{
- return DisplayCompilationException(errorContext.HttpContext, compilationException);
+ httpContext.Response.ContentType = "text/plain";
+
+ var sb = new StringBuilder();
+ sb.AppendLine(errorContext.Exception.ToString());
+ sb.AppendLine();
+ sb.AppendLine("HEADERS");
+ sb.AppendLine("=======");
+ foreach (var pair in httpContext.Request.Headers)
+ {
+ sb.AppendLine($"{pair.Key}: {pair.Value}");
+ }
+
+ return httpContext.Response.WriteAsync(sb.ToString());
}
- return DisplayRuntimeException(errorContext.HttpContext, errorContext.Exception);
+ if (errorContext.Exception is ICompilationException compilationException)
+ {
+ return DisplayCompilationException(httpContext, compilationException);
+ }
+
+ return DisplayRuntimeException(httpContext, errorContext.Exception);
}
private Task DisplayCompilationException(
diff --git a/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs
index 8552a2da8e..26bda1074d 100644
--- a/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs
+++ b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -45,6 +46,58 @@ namespace Microsoft.AspNetCore.Diagnostics
Assert.Null(listener.DiagnosticHandledException?.Exception);
}
+ [Fact]
+ public async Task ErrorPageWithAcceptHeaderForHtmlReturnsHtml()
+ {
+ // Arrange
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new Exception("Test exception");
+ });
+ });
+ var server = new TestServer(builder);
+
+ // Act
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
+ var response = await client.GetAsync("/path");
+
+ // Assert
+ var responseText = await response.Content.ReadAsStringAsync();
+ Assert.Equal("text/html", response.Content.Headers.ContentType.MediaType);
+ Assert.Contains("
+ {
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new Exception("Test exception");
+ });
+ });
+ var server = new TestServer(builder);
+
+ // Act
+ var response = await server.CreateClient().GetAsync("/path");
+
+ // Assert
+ var responseText = await response.Content.ReadAsStringAsync();
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType);
+ Assert.Contains("Test exception", responseText);
+ Assert.DoesNotContain(" builder.ConfigureLogging(l => l.Services.AddSingleton(loggerProvider)))
.CreateDefaultClient();
+ // These tests want to verify runtime compilation and formatting in the HTML of the error page
+ Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
}
public HttpClient Client { get; }