Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to AbpExceptionHandlingOptions for enabling/disabling StackTrace #10594

Merged
merged 3 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions docs/en/Exception-Handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,20 @@ Some exception types are automatically thrown by the framework:

You can also throw these type of exceptions in your code (although it's rarely needed).

## Send exception details to the client
## AbpExceptionHandlingOptions

You can send exceptions to the client via the `SendExceptionsDetailsToClients` property of the `AbpExceptionHandlingOptions` class:
`AbpExceptionHandlingOptions` is the main [options object](Options.md) to configure the exception handling system. You can configure it in the `ConfigureServices` method of your [module](Module-Development-Basics.md):

````csharp
services.Configure<AbpExceptionHandlingOptions>(options =>
Configure<AbpExceptionHandlingOptions>(options =>
{
options.SendExceptionsDetailsToClients = true;
options.SendStackTraceToClients = false;
});
````

Here, a list of the options you can configure:

* `SendExceptionsDetailsToClients` (default: `false`): You can enable or disable sending exception details to the client.
* `SendStackTraceToClients` (default: `true`): You can enable or disable sending the stack trace of exception to the client. If you want to send the stack trace to the client, you must set both `SendStackTraceToClients` and `SendExceptionsDetailsToClients` options to `true` otherwise, the stack trace will not be sent to the client.

Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ public async Task InformAsync(UserExceptionInformerContext context)

protected virtual RemoteServiceErrorInfo GetErrorInfo(UserExceptionInformerContext context)
{
return ExceptionToErrorInfoConverter.Convert(context.Exception, Options.SendExceptionsDetailsToClients);
return ExceptionToErrorInfoConverter.Convert(context.Exception, options =>
{
options.SendExceptionsDetailsToClients = Options.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = Options.SendStackTraceToClients;
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public async Task<IActionResult> Index(int httpStatusCode)

await _exceptionNotifier.NotifyAsync(new ExceptionNotificationContext(exception));

var errorInfo = _errorInfoConverter.Convert(exception, _exceptionHandlingOptions.SendExceptionsDetailsToClients);
var errorInfo = _errorInfoConverter.Convert(exception, options =>
{
options.SendExceptionsDetailsToClients = _exceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = _exceptionHandlingOptions.SendStackTraceToClients;
});

if (httpStatusCode == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ protected virtual async Task HandleAndWrapException(ExceptionContext context)

var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, options =>
{
options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients;
});

var logLevel = context.Exception.GetLogLevel();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ protected virtual async Task HandleAndWrapException(PageHandlerExecutedContext c

var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, options =>
{
options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients;
});

var logLevel = context.Exception.GetLogLevel();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ await httpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionH
var errorInfoConverter = httpContext.RequestServices.GetRequiredService<IExceptionToErrorInfoConverter>();
var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionHandlingOptions = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;

httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
Expand All @@ -86,7 +86,11 @@ await httpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionH
await httpContext.Response.WriteAsync(
jsonSerializer.Serialize(
new RemoteServiceErrorResponse(
errorInfoConverter.Convert(exception, options.SendExceptionsDetailsToClients)
errorInfoConverter.Convert(exception, options =>
{
options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients;
})
)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
public class AbpExceptionHandlingOptions
{
public bool SendExceptionsDetailsToClients { get; set; } = false;

public bool SendStackTraceToClients { get; set; } = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConvert

public RemoteServiceErrorInfo Convert(Exception exception, bool includeSensitiveDetails)
{
var errorInfo = CreateErrorInfoWithoutCode(exception, includeSensitiveDetails);
var exceptionHandlingOptions = CreateDefaultOptions();
exceptionHandlingOptions.SendExceptionsDetailsToClients = includeSensitiveDetails;
exceptionHandlingOptions.SendStackTraceToClients = includeSensitiveDetails;

var errorInfo = CreateErrorInfoWithoutCode(exception, exceptionHandlingOptions);

if (exception is IHasErrorCode hasErrorCodeException)
{
Expand All @@ -50,11 +54,26 @@ public RemoteServiceErrorInfo Convert(Exception exception, bool includeSensitive
return errorInfo;
}

protected virtual RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception, bool includeSensitiveDetails)
public RemoteServiceErrorInfo Convert(Exception exception, Action<AbpExceptionHandlingOptions> options = null)
{
if (includeSensitiveDetails)
var exceptionHandlingOptions = CreateDefaultOptions();
options?.Invoke(exceptionHandlingOptions);

var errorInfo = CreateErrorInfoWithoutCode(exception, exceptionHandlingOptions);

if (exception is IHasErrorCode hasErrorCodeException)
{
errorInfo.Code = hasErrorCodeException.Code;
}

return errorInfo;
}

protected virtual RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception, AbpExceptionHandlingOptions options)
{
if (options.SendExceptionsDetailsToClients)
{
return CreateDetailedErrorInfoFromException(exception);
return CreateDetailedErrorInfoFromException(exception, options.SendStackTraceToClients);
}

exception = TryToGetActualException(exception);
Expand Down Expand Up @@ -194,11 +213,11 @@ protected virtual Exception TryToGetActualException(Exception exception)
return exception;
}

protected virtual RemoteServiceErrorInfo CreateDetailedErrorInfoFromException(Exception exception)
protected virtual RemoteServiceErrorInfo CreateDetailedErrorInfoFromException(Exception exception, bool sendStackTraceToClients)
{
var detailBuilder = new StringBuilder();

AddExceptionToDetails(exception, detailBuilder);
AddExceptionToDetails(exception, detailBuilder, sendStackTraceToClients);

var errorInfo = new RemoteServiceErrorInfo(exception.Message, detailBuilder.ToString());

Expand All @@ -210,7 +229,7 @@ protected virtual RemoteServiceErrorInfo CreateDetailedErrorInfoFromException(Ex
return errorInfo;
}

protected virtual void AddExceptionToDetails(Exception exception, StringBuilder detailBuilder)
protected virtual void AddExceptionToDetails(Exception exception, StringBuilder detailBuilder, bool sendStackTraceToClients)
{
//Exception Message
detailBuilder.AppendLine(exception.GetType().Name + ": " + exception.Message);
Expand All @@ -237,15 +256,15 @@ protected virtual void AddExceptionToDetails(Exception exception, StringBuilder
}

//Exception StackTrace
if (!string.IsNullOrEmpty(exception.StackTrace))
if (sendStackTraceToClients && !string.IsNullOrEmpty(exception.StackTrace))
{
detailBuilder.AppendLine("STACK TRACE: " + exception.StackTrace);
}

//Inner exception
if (exception.InnerException != null)
{
AddExceptionToDetails(exception.InnerException, detailBuilder);
AddExceptionToDetails(exception.InnerException, detailBuilder, sendStackTraceToClients);
}

//Inner exceptions for AggregateException
Expand All @@ -259,7 +278,7 @@ protected virtual void AddExceptionToDetails(Exception exception, StringBuilder

foreach (var innerException in aggException.InnerExceptions)
{
AddExceptionToDetails(innerException, detailBuilder);
AddExceptionToDetails(innerException, detailBuilder, sendStackTraceToClients);
}
}
}
Expand Down Expand Up @@ -296,5 +315,14 @@ protected virtual string GetValidationErrorNarrative(IHasValidationErrors valida

return detailBuilder.ToString();
}

protected virtual AbpExceptionHandlingOptions CreateDefaultOptions()
{
return new AbpExceptionHandlingOptions
{
SendExceptionsDetailsToClients = false,
SendStackTraceToClients = true
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,18 @@ public interface IExceptionToErrorInfoConverter
/// <summary>
/// Converter method.
/// </summary>
/// <param name="exception">The exception</param>
/// <param name="exception">The exception.</param>
/// <param name="includeSensitiveDetails">Should include sensitive details to the error info?</param>
/// <returns>Error info or null</returns>
[Obsolete("Use other Convert method.")]
RemoteServiceErrorInfo Convert(Exception exception, bool includeSensitiveDetails);

/// <summary>
/// Converter method.
/// </summary>
/// <param name="exception">The exception.</param>
/// <param name="options">Additional options.</param>
/// <returns>Error info or null</returns>
RemoteServiceErrorInfo Convert(Exception exception, Action<AbpExceptionHandlingOptions> options = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,27 @@ public void Test_AbpAuthorizationException_Localization()
using (CultureHelper.Use("zh-Hans"))
{
var exception = new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenPolicyHasNotGranted);
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("授权失败! 提供的策略尚未授予.");

exception = new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenPolicyHasNotGrantedWithPolicyName)
.WithData("PolicyName", "my_policy_name");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("授权失败! 提供的策略尚未授予: my_policy_name");

exception = new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenPolicyHasNotGrantedForGivenResource)
.WithData("ResourceName", "my_resource_name");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("授权失败! 提供的策略未授予提供的资源: my_resource_name");

exception = new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenRequirementHasNotGrantedForGivenResource)
.WithData("ResourceName", "my_resource_name");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("授权失败! 提供的要求未授予提供的资源: my_resource_name");

exception = new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenRequirementsHasNotGrantedForGivenResource)
.WithData("ResourceName", "my_resource_name");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("授权失败! 提供的要求未授予提供的资源: my_resource_name");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ public void Test_AbpAuthorizationException_Localization()
{
var exception = new AbpAuthorizationException(code: AbpFeatureErrorCodes.FeatureIsNotEnabled)
.WithData("FeatureName", "my_feature_name");
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("功能未启用: my_feature_name");

exception = new AbpAuthorizationException(code: AbpFeatureErrorCodes.AllOfTheseFeaturesMustBeEnabled)
.WithData("FeatureNames", "my_feature_name, my_feature_name2");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("必要的功能未启用. 这些功能需要启用: my_feature_name, my_feature_name2");

exception = new AbpAuthorizationException(code: AbpFeatureErrorCodes.AtLeastOneOfTheseFeaturesMustBeEnabled)
.WithData("FeatureNames", "my_feature_name, my_feature_name2");
errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("必要的功能未启用. 需要启用这些功能中的一项:my_feature_name, my_feature_name2");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void AbpAuthorizationException_Localization()
var exception = new AbpGlobalFeatureNotEnabledException(code: AbpGlobalFeatureErrorCodes.GlobalFeatureIsNotEnabled)
.WithData("ServiceName", "MyService")
.WithData("GlobalFeatureName", "TestFeature");;
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception, false);
var errorInfo = _exceptionToErrorInfoConverter.Convert(exception);
errorInfo.Message.ShouldBe("'MyService'服务需要启用'TestFeature'功能.");
}
}
Expand Down