throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]);
Then define it in thelocalization resource for each language. Example:
{ "culture": "en", "texts": { "UserNameShouldBeUniqueMessage": "Username should be unique!" }}
String localizer already supportsparameterized messages. For example:
throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage", "john"]);
Then the localization text can be:
"UserNameShouldBeUniqueMessage": "Username should be unique! '{0}' is already taken!"
TheIUserFriendlyException interface is derived from theIBusinessException and theUserFriendlyException class is derived from theBusinessException class.
Using Error Codes
UserFriendlyException is fine, but it has a few problems in advanced usages:
It requires you toinject the string localizer everywhere and always use it while throwing exceptions.
However, in some of the cases, it maynot be possible to inject the string localizer (in a static context or in an entity method).
Instead of localizing the message while throwing the exception, you can separate the process usingerror codes.
First, define thecode-namespace tolocalization resource mapping in the module configuration:
Then any of the exceptions withVolo.Qa namespace will be localized using their given localization resource. The localization resource should always have an entry with the error code key. Example:
{ "culture": "en", "texts": { "Volo.Qa:010002": "You can not vote your own answer!" }}
Then a business exception can be thrown with the error code:
throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer);
Throwing any exception implementing theIHasErrorCode interface behaves the same. So, the error code localization approach is not unique to theBusinessException class.
Defining localized string is not required for an error message. If it's not defined, ABP sends the default error message to the client. It does not use theMessage property of the exception! if you want that, use theUserFriendlyException (or use an exception type that implements theIUserFriendlyException interface).
Using Message Parameters
If you have a parameterized error message, then you can set it with the exception'sData property. For example:
throw new BusinessException("App:010046"){ Data = { {"UserName", "john"} }};
Fortunately there is a shortcut way to code this:
throw new BusinessException("App:010046") .WithData("UserName", "john");
Then the localized text can contain theUserName parameter:
{ "culture": "en", "texts": { "App:010046": "Username should be unique. '{UserName}' is already taken!" }}
WithData can be chained with more than one parameter (like.WithData(...).WithData(...)).
HTTP Status Code Mapping
ABP tries to automatically determine the most suitable HTTP status code for common exception types by following these rules:
For theAbpAuthorizationException:
Returns401 (unauthorized) if user has not logged in.
Returns403 (forbidden) if user has logged in.
Returns400 (bad request) for theAbpValidationException.
Returns404 (not found) for theEntityNotFoundException.
Returns403 (forbidden) for theIBusinessException (andIUserFriendlyException since it extends theIBusinessException).
Returns501 (not implemented) for theNotImplementedException.
Returns500 (internal server error) for other exceptions (those are assumed as infrastructure exceptions).
TheIHttpExceptionStatusCodeFinder is used to automatically determine the HTTP status code. The default implementation is theDefaultHttpExceptionStatusCodeFinder class. It can be replaced or extended as needed.
Custom Mappings
Automatic HTTP status code determination can be overrided by custom mappings. For example:
It is possible to be informed when the ABPhandles an exception. It automaticallylogs all the exceptions to the standardlogger, but you may want to do more.
In this case, create a class derived from theExceptionSubscriber class in your application:
public class MyExceptionSubscriber : ExceptionSubscriber{ public async override Task HandleAsync(ExceptionNotificationContext context) { //TODO... }}
Thecontext object contains necessary information about the exception occurred.
You can have multiple subscribers, each gets a copy of the exception. Exceptions thrown by your subscriber is ignored (but still logged).
Built-In Exceptions
Some exception types are automatically thrown by the framework:
AbpAuthorizationException is thrown if the current user has no permission to perform the requested operation. Seeauthorization for more.
AbpValidationException is thrown if the input of the current request is not valid. Seevalidation for more.
EntityNotFoundException is thrown if the requested entity is not available. This is mostly thrown byrepositories.
You can also throw these type of exceptions in your code (although it's rarely needed).
AbpExceptionHandlingOptions
AbpExceptionHandlingOptions is the mainoptions object to configure the exception handling system. You can configure it in theConfigureServices method of yourmodule:
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 bothSendStackTraceToClients andSendExceptionsDetailsToClients options totrue otherwise, the stack trace will not be sent to the client.