Open Closed

How should we customize exception message of Check.NotNull() class for specific cases ? #2092


User avatar
0
selinkoykiran created

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)
  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    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 in MyProjectName.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.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    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 from AbpPageModel) 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 👋

Made with ❤️ on ABP v9.1.0-preview. Updated on December 10, 2024, 06:38