Hello ,
We are using abp Check class for null or whitespace conditions in all layers and we saw that when a validation error happens in the domain side while checking, user will see the below exception :
We are using this check class in every layers of our application like in domain , application, web. By the way If we use validators on web, application layer , we can see more reasonable messages (because when using validators, abp wraps this kind of check exceptions inside AbpValidationException, so we could see validation exception messages on swagger for example. ) But for example we are using Check class in our domain layer and we can't or don't use any validator , in that case how can we customize this exception message ? Do we need to wrap, all Check class used code lines with try catch blocks at everywhere ? Is there any option for this or do we need to write custom exception handling middleware for that ?
Note = By the way we've tried to use string message option of Check.NotNull() method. But it just wrote this custom message into the log file, An internal error occurred during your request message did not change.
Thank you.
Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.
If you're creating a bug/problem report, please include followings:
- ABP Framework version: v4.4..0
- UI type: MVC
- DB provider: EF Core
- **Tiered (MVC) : yes
- Exception message and stack trace:
- Steps to reproduce the issue:"
2 Answer(s)
-
0
Hi,
We don't need to write a custom exception handling middleware for this, but instead we need to override ABP's DefaultExceptionToErrorInfoConverter.
Based on what I understand from your needs, I created a folder named
ExceptionHandling
inMyProjectName.Domain
and put the following code in it.[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IExceptionToErrorInfoConverter))] public class MyExceptionToErrorInfoConverter : DefaultExceptionToErrorInfoConverter, IExceptionToErrorInfoConverter { public MyExceptionToErrorInfoConverter(IOptions<AbpExceptionLocalizationOptions> localizationOptions, IStringLocalizerFactory stringLocalizerFactory, IStringLocalizer<AbpExceptionHandlingResource> stringLocalizer, IServiceProvider serviceProvider) : base(localizationOptions, stringLocalizerFactory, stringLocalizer, serviceProvider) { } protected override RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception, bool includeSensitiveDetails) { if (includeSensitiveDetails) { return CreateDetailedErrorInfoFromException(exception); } exception = TryToGetActualException(exception); if (exception is AbpRemoteCallException remoteCallException) { return remoteCallException.Error; } if (exception is AbpDbConcurrencyException) { return new RemoteServiceErrorInfo(L["AbpDbConcurrencyErrorMessage"]); } if (exception is EntityNotFoundException) { return CreateEntityNotFoundError(exception as EntityNotFoundException); } var errorInfo = new RemoteServiceErrorInfo(); if (exception is IUserFriendlyException or ArgumentNullException) // Here ArgumentNullException { errorInfo.Message = exception.Message; errorInfo.Details = (exception as IHasErrorDetails)?.Details; } if (exception is IHasValidationErrors) { if (errorInfo.Message.IsNullOrEmpty()) { errorInfo.Message = L["ValidationErrorMessage"]; } if (errorInfo.Details.IsNullOrEmpty()) { errorInfo.Details = GetValidationErrorNarrative(exception as IHasValidationErrors); } errorInfo.ValidationErrors = GetValidationErrorInfos(exception as IHasValidationErrors); } TryToLocalizeExceptionMessage(exception, errorInfo); if (errorInfo.Message.IsNullOrEmpty()) { errorInfo.Message = L["InternalServerErrorMessage"]; } errorInfo.Data = exception.Data; return errorInfo; } }
The only difference from the default
CreateErrorInfoWithoutCode
method is the line with the comment line.Now you can see
ArgumentException
thrown in domain. Of course, you can customize how you want to see it according to your needs.I hope my answer helps in customizing to your needs.
-
0
In the meantime, I'd like to share some information for more elegant error handling.
In general, we do not want users to go to the Error page, so I generally prefer to use a method like the one below.
I am creating a method inside
MyProjectNamePageModel
(class inherited fromAbpPageModel
) as below.protected void ShowAlert(Exception exception) { Logger.LogException(exception); var errorInfoConverter = LazyServiceProvider.LazyGetRequiredService<IExceptionToErrorInfoConverter>(); var errorInfo = errorInfoConverter.Convert(exception, false); Alerts.Danger(errorInfo.Message); }
The
errorInfoConverter.Convert
method here makes the message of a BusinessException(or others Abp's exceptions) thrown in the domain appear in the UI, if you don't need it, you can remove it.Then, with a code like the following, we ensure that the user stays on this page instead of a separate page when the error is thrown.
public async Task<IActionResult> OnPostAsync() { try { ValidateModel(); //Your CODE return RedirectToPage("/"); } catch (Exception exception) { ShowAlert(exception); return Page(); } }
For more information you can check here.
You probably already know, but I still wanted to share this information in case it helps users who have the same problem, I hope it helps 👋