#82 - Improve error handling mechanics.

This commit is contained in:
Chris Ross 2014-11-20 16:14:10 -08:00
parent 8b7d33baaf
commit fec32f6746
2 changed files with 81 additions and 18 deletions

View File

@ -60,6 +60,8 @@ namespace Microsoft.AspNet.Security.Infrastructure
public IAuthenticationHandler PriorHandler { get; set; } public IAuthenticationHandler PriorHandler { get; set; }
public bool Faulted { get; set; }
protected async Task BaseInitializeAsync(AuthenticationOptions options, HttpContext context) protected async Task BaseInitializeAsync(AuthenticationOptions options, HttpContext context)
{ {
_baseOptions = options; _baseOptions = options;
@ -98,8 +100,25 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// </summary> /// </summary>
/// <returns>async completion</returns> /// <returns>async completion</returns>
internal async Task TeardownAsync() internal async Task TeardownAsync()
{
try
{ {
await ApplyResponseAsync(); await ApplyResponseAsync();
}
catch (Exception)
{
try
{
await TeardownCoreAsync();
}
catch (Exception)
{
// Don't mask the original exception
}
UnregisterAuthenticationHandler();
throw;
}
await TeardownCoreAsync(); await TeardownCoreAsync();
UnregisterAuthenticationHandler(); UnregisterAuthenticationHandler();
} }
@ -216,6 +235,13 @@ namespace Microsoft.AspNet.Security.Infrastructure
} }
private void ApplyResponse() private void ApplyResponse()
{
// If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
// scenarios like the ability to display the error page or re-execute the request.
try
{
if (!Faulted)
{ {
LazyInitializer.EnsureInitialized( LazyInitializer.EnsureInitialized(
ref _applyResponse, ref _applyResponse,
@ -227,6 +253,13 @@ namespace Microsoft.AspNet.Security.Infrastructure
return Task.FromResult(0); return Task.FromResult(0);
}).GetAwaiter().GetResult(); // Block if the async version is in progress. }).GetAwaiter().GetResult(); // Block if the async version is in progress.
} }
}
catch (Exception)
{
Faulted = true;
throw;
}
}
protected virtual void ApplyResponseCore() protected virtual void ApplyResponseCore()
{ {
@ -240,14 +273,28 @@ namespace Microsoft.AspNet.Security.Infrastructure
/// or later, as the last step when the original async call to the middleware is returning. /// or later, as the last step when the original async call to the middleware is returning.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private Task ApplyResponseAsync() private async Task ApplyResponseAsync()
{ {
return LazyInitializer.EnsureInitialized( // If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
// scenarios like the ability to display the error page or re-execute the request.
try
{
if (!Faulted)
{
await LazyInitializer.EnsureInitialized(
ref _applyResponse, ref _applyResponse,
ref _applyResponseInitialized, ref _applyResponseInitialized,
ref _applyResponseSyncLock, ref _applyResponseSyncLock,
ApplyResponseCoreAsync); ApplyResponseCoreAsync);
} }
}
catch (Exception)
{
Faulted = true;
throw;
}
}
/// <summary> /// <summary>
/// Core method that may be overridden by handler. The default behavior is to call two common response /// Core method that may be overridden by handler. The default behavior is to call two common response

View File

@ -41,10 +41,26 @@ namespace Microsoft.AspNet.Security.Infrastructure
{ {
AuthenticationHandler<TOptions> handler = CreateHandler(); AuthenticationHandler<TOptions> handler = CreateHandler();
await handler.Initialize(Options, context); await handler.Initialize(Options, context);
try
{
if (!await handler.InvokeAsync()) if (!await handler.InvokeAsync())
{ {
await _next(context); await _next(context);
} }
}
catch (Exception)
{
try
{
handler.Faulted = true;
await handler.TeardownAsync();
}
catch (Exception)
{
// Don't mask the original exception
}
throw;
}
await handler.TeardownAsync(); await handler.TeardownAsync();
} }
} }