#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;
@ -94,12 +96,29 @@ namespace Microsoft.AspNet.Security.Infrastructure
} }
/// <summary> /// <summary>
/// Called once per request after Initialize and Invoke. /// Called once per request after Initialize and Invoke.
/// </summary> /// </summary>
/// <returns>async completion</returns> /// <returns>async completion</returns>
internal async Task TeardownAsync() internal async Task TeardownAsync()
{ {
await ApplyResponseAsync(); try
{
await ApplyResponseAsync();
}
catch (Exception)
{
try
{
await TeardownCoreAsync();
}
catch (Exception)
{
// Don't mask the original exception
}
UnregisterAuthenticationHandler();
throw;
}
await TeardownCoreAsync(); await TeardownCoreAsync();
UnregisterAuthenticationHandler(); UnregisterAuthenticationHandler();
} }
@ -217,15 +236,29 @@ namespace Microsoft.AspNet.Security.Infrastructure
private void ApplyResponse() private void ApplyResponse()
{ {
LazyInitializer.EnsureInitialized( // If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
ref _applyResponse, // failed task is cached. If called again the same error will be re-thrown. This breaks error handling
ref _applyResponseInitialized, // scenarios like the ability to display the error page or re-execute the request.
ref _applyResponseSyncLock, try
() => {
if (!Faulted)
{ {
ApplyResponseCore(); LazyInitializer.EnsureInitialized(
return Task.FromResult(0); ref _applyResponse,
}).GetAwaiter().GetResult(); // Block if the async version is in progress. ref _applyResponseInitialized,
ref _applyResponseSyncLock,
() =>
{
ApplyResponseCore();
return Task.FromResult(0);
}).GetAwaiter().GetResult(); // Block if the async version is in progress.
}
}
catch (Exception)
{
Faulted = true;
throw;
}
} }
protected virtual void ApplyResponseCore() protected virtual void ApplyResponseCore()
@ -240,13 +273,27 @@ 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
ref _applyResponse, // failed task is cached. If called again the same error will be re-thrown. This breaks error handling
ref _applyResponseInitialized, // scenarios like the ability to display the error page or re-execute the request.
ref _applyResponseSyncLock, try
ApplyResponseCoreAsync); {
if (!Faulted)
{
await LazyInitializer.EnsureInitialized(
ref _applyResponse,
ref _applyResponseInitialized,
ref _applyResponseSyncLock,
ApplyResponseCoreAsync);
}
}
catch (Exception)
{
Faulted = true;
throw;
}
} }
/// <summary> /// <summary>

View File

@ -41,9 +41,25 @@ namespace Microsoft.AspNet.Security.Infrastructure
{ {
AuthenticationHandler<TOptions> handler = CreateHandler(); AuthenticationHandler<TOptions> handler = CreateHandler();
await handler.Initialize(Options, context); await handler.Initialize(Options, context);
if (!await handler.InvokeAsync()) try
{ {
await _next(context); if (!await handler.InvokeAsync())
{
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();
} }