New ErrorHandler middleware.
This commit is contained in:
parent
3279c15047
commit
a0f3560095
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.22130.0
|
||||
VisualStudioVersion = 14.0.22129.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{509A6F36-AD80-4A18-B5B1-717D38DFF29D}"
|
||||
EndProject
|
||||
|
|
@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2AF90579-B
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Tests", "test\Microsoft.AspNet.Diagnostics.Tests\Microsoft.AspNet.Diagnostics.Tests.kproj", "{994351B4-7B2A-4139-8B72-72C5BB5CC618}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ErrorHandlerSample", "samples\ErrorHandlerSample\ErrorHandlerSample.kproj", "{427CDB36-78B0-4583-9EBC-7F283DE60355}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -96,6 +98,16 @@ Global
|
|||
{994351B4-7B2A-4139-8B72-72C5BB5CC618}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{994351B4-7B2A-4139-8B72-72C5BB5CC618}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{994351B4-7B2A-4139-8B72-72C5BB5CC618}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -107,5 +119,6 @@ Global
|
|||
{4D4A785A-ECB9-4916-A88F-0FD306EE3B74} = {509A6F36-AD80-4A18-B5B1-717D38DFF29D}
|
||||
{CD62A191-39F5-4C86-BC1D-7731085120F5} = {ACAA0157-A8C4-4152-93DE-90CCDF304087}
|
||||
{994351B4-7B2A-4139-8B72-72C5BB5CC618} = {2AF90579-B118-4583-AE88-672EFACB5BC4}
|
||||
{427CDB36-78B0-4583-9EBC-7F283DE60355} = {ACAA0157-A8C4-4152-93DE-90CCDF304087}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>427cdb36-78b0-4583-9ebc-7f283de60355</ProjectGuid>
|
||||
<OutputType>Web</OutputType>
|
||||
<RootNamespace>ErrorHandlerSample</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<DevelopmentServerPort>54230</DevelopmentServerPort>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Diagnostics;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace ErrorHandlerSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// Configure the error handler to show an error page.
|
||||
app.UseErrorHandler(errorApp =>
|
||||
{
|
||||
// Normally you'd use MVC or similar to render a nice page.
|
||||
errorApp.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync("<html><body>\r\n");
|
||||
await context.Response.WriteAsync("We're sorry, we encountered an un-expected issue with your application.<br>\r\n");
|
||||
|
||||
var error = context.GetFeature<IErrorHandlerFeature>();
|
||||
if (error != null)
|
||||
{
|
||||
// This error would not normally be exposed to the client
|
||||
await context.Response.WriteAsync("<br>Error: " + System.Net.WebUtility.HtmlEncode(error.Error.Message) + "<br>\r\n");
|
||||
}
|
||||
await context.Response.WriteAsync("<br><a href=\"/\">Home</a><br>\r\n");
|
||||
await context.Response.WriteAsync("</body></html>\r\n");
|
||||
await context.Response.WriteAsync(new string(' ', 512)); // Padding for IE
|
||||
});
|
||||
});
|
||||
|
||||
// We could also configure it to re-execute the request on the normal pipeline with a different path.
|
||||
// app.UseErrorHandler("/error.html");
|
||||
|
||||
// The broken section of our application.
|
||||
app.Map("/throw", throwApp =>
|
||||
{
|
||||
throwApp.Run(context => { throw new Exception("Application Exception"); });
|
||||
});
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
// The home page.
|
||||
app.Run(async context =>
|
||||
{
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync("<html><body>Welcome to the sample<br><br>\r\n");
|
||||
await context.Response.WriteAsync("Click here to throw an exception: <a href=\"/throw\">throw</a>\r\n");
|
||||
await context.Response.WriteAsync("</body></html>\r\n");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"webroot": "wwwroot",
|
||||
"exclude": "wwwroot/**/*.*",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
|
||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
|
||||
},
|
||||
"commands": {
|
||||
/* Change the port number when you are self hosting this application */
|
||||
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
|
||||
},
|
||||
"frameworks": {
|
||||
"aspnet50" : { },
|
||||
"aspnetcore50" : { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
You've reached the static error page.<br /><br />
|
||||
<a href="/">Home</a><br />
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Diagnostics;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Builder
|
||||
{
|
||||
public static class ErrorHandlerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request.
|
||||
/// The request will not be re-executed if the response has already started.
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="errorHandlingPath"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, string errorHandlingPath)
|
||||
{
|
||||
var options = new ErrorHandlerOptions()
|
||||
{
|
||||
ErrorHandlingPath = new PathString(errorHandlingPath)
|
||||
};
|
||||
return app.UseMiddleware<ErrorHandlerMiddleware>(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.
|
||||
/// The request will not be re-executed if the response has already started.
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="configure"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure)
|
||||
{
|
||||
var subAppBuilder = app.New();
|
||||
configure(subAppBuilder);
|
||||
var errorPipeline = subAppBuilder.Build();
|
||||
var options = new ErrorHandlerOptions()
|
||||
{
|
||||
ErrorHandler = errorPipeline
|
||||
};
|
||||
return app.UseMiddleware<ErrorHandlerMiddleware>(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Diagnostics
|
||||
{
|
||||
public class ErrorHandlerFeature : IErrorHandlerFeature
|
||||
{
|
||||
public Exception Error { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Diagnostics
|
||||
{
|
||||
public class ErrorHandlerMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ErrorHandlerOptions _options;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ErrorHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ErrorHandlerOptions options)
|
||||
{
|
||||
_next = next;
|
||||
_options = options;
|
||||
_logger = loggerFactory.Create<ErrorHandlerMiddleware>();
|
||||
if (_options.ErrorHandler == null)
|
||||
{
|
||||
_options.ErrorHandler = _next;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var responseStarted = false;
|
||||
try
|
||||
{
|
||||
context.Response.OnSendingHeaders(state => responseStarted = true, null);
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.WriteError("An unhandled exception has occurred: " + ex.Message, ex);
|
||||
// We can't do anything if the response has already started, just abort.
|
||||
if (responseStarted)
|
||||
{
|
||||
_logger.WriteWarning("The response has already started, the error handler will not be executed.");
|
||||
throw;
|
||||
}
|
||||
|
||||
PathString originalPath = context.Request.Path;
|
||||
if (_options.ErrorHandlingPath.HasValue)
|
||||
{
|
||||
context.Request.Path = _options.ErrorHandlingPath;
|
||||
}
|
||||
try
|
||||
{
|
||||
var errorHandlerFeature = new ErrorHandlerFeature()
|
||||
{
|
||||
Error = ex,
|
||||
};
|
||||
context.SetFeature<IErrorHandlerFeature>(errorHandlerFeature);
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.Headers.Clear();
|
||||
// TODO: Try clearing any buffered data. The buffering feature/middleware has not been designed yet.
|
||||
await _options.ErrorHandler(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.WriteError("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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Diagnostics
|
||||
{
|
||||
public class ErrorHandlerOptions
|
||||
{
|
||||
public PathString ErrorHandlingPath { get; set; }
|
||||
|
||||
public RequestDelegate ErrorHandler { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.Diagnostics
|
||||
{
|
||||
[AssemblyNeutral]
|
||||
public interface IErrorHandlerFeature
|
||||
{
|
||||
Exception Error { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
"Microsoft.AspNet.FeatureModel": "1.0.0-*",
|
||||
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
||||
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
|
||||
"Microsoft.Framework.Logging": "1.0.0-*",
|
||||
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" }
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue