@ -173,4 +173,4 @@ ABP does not force you to apply any DDD rule or patterns. However, it tries to m
#### Aggregate Roots with Composite Keys
While it's not common (and not suggested) for aggregate roots, it isin fact possible to define composite keys in the same way as defined for the mentioned entities above. Use non-generic `AggregateRoot` base class in that case.
While it's not common (and not suggested) for aggregate roots, it isin fact possible to define composite keys in the same way as defined for the mentioned entities above. Use non-generic `AggregateRoot` base class in that case.
ABP provides a built-in infrastructure and offers a standard model to exception handling for a web application.
ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application.
* Automatically **handles all exceptions** and sends a standard **formatted error message** to the client for an API/AJAX request.
* Automatically hides **internal infrastructure errors** and returns a standard error message.
* Provides configuration option to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides configuration option to map custom exceptions.
* Provides a configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions.
### Automatic Exception Handling
`AbpExceptionFilter` handles an exception if **any of the following conditions**meets;
`AbpExceptionFilter` handles an exception if **any of the following conditions**are meet:
* Exception is thrown by a **controller action** which returns an **object result** (not a view result).
* The request is an AJAX request (`X-Requested-With` HTTP header value is `XMLHttpRequest`).
@ -29,7 +29,7 @@ Error Message is an instance of the `RemoteServiceErrorResponse` class. The simp
}
````
There are **optional fields** those can be filled based on the occurred exception.
There are **optional fields** those can be filled based upon the exception that has occured.
##### Error Code
@ -81,7 +81,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
}
````
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't deal with validation errors except you have a very custom validation logic.
`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic.
#### Logging
@ -89,7 +89,7 @@ Caught exceptions are automatically logged.
##### Log Level
Exceptions are logged with the `Error` level by default. Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example:
Exceptions are logged with the `Error` level by default. The Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example:
````C#
public class MyException : Exception, IHasLogLevel
@ -120,15 +120,15 @@ public class MyException : Exception, IExceptionWithSelfLogging
Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception.
`BusinessException` implements the `IBusinessException` interface in addition to the `IHasErrorCode`, `IHasErrorDetails` and `IHasLogLevel` interfaces. Default log level is `Warning`.
`BusinessException` implements the `IBusinessException` interface in addition to the `IHasErrorCode`, `IHasErrorDetails` and `IHasLogLevel` interfaces. The default log level is `Warning`.
Usually you have an error code related to a particular business exception. Example:
Usually you have an error code related to a particular business exception. For example:
````C#
throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer);
````
`QaErrorCodes.CanNotVoteYourOwnAnswer` is just a `const string`. Such an error code format is suggested:
`QaErrorCodes.CanNotVoteYourOwnAnswer` is just a `const string`. The following error code format is recommended:
````
<code-namespace>:<error-code>
@ -147,11 +147,11 @@ Volo.Qa:010002
### Exception Localization
One problem with throwing exception is how to localize error messages while sending to clients. ABP offers two models and their variants.
One problem with throwing exceptions is how to localize error messages while sending it to the client. ABP offers two models and their variants.
#### User Friendly Exception
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send to the client.
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client.
`UserFriendlyException` class is the built-in implementation of the `IUserFriendlyException` interface. Example usage:
@ -161,7 +161,7 @@ throw new UserFriendlyException(
);
````
In this way, **no need to localization** at all. If you want to localize the message, you can inject and use the standard **string localizer** (see the [localization document](Localization.md)). Example:
In this way, there is **no need for localization** at all. If you want to localize the message, you can inject and use the standard **string localizer** (see the [localization document](Localization.md)). Example:
````C#
throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]);
@ -178,7 +178,7 @@ Then define it in the **localization resource** for each language. Example:
Then any of the exceptions with `Volo.Qa` namespace will be localized using the given localization resource. The localization resource should have an entry with the error code key. Example:
Then any of the exceptions with `Volo.Qa` namespace will be localized using their given localization resource. The localization resource should always have an entry with the error code key. Example:
````json
{
@ -228,11 +228,11 @@ throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer);
````
* Throwing any exception implementing the `IHasErrorCode` interface behaves the same. So, the error code localization approach is not unique to the `BusinessException` class.
* Defining localized string is not required for an error message. If not defined, ABP sends the default error message to the client. It does not use the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface).
* 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 the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface).
##### Using Message Parameters
If you have a parameterized error message, then you can set it with the exception's `Data` property. Example:
If you have a parameterized error message, then you can set it with the exception's `Data` property. For example:
````C#
throw new BusinessException("App:010046")
@ -245,14 +245,14 @@ throw new BusinessException("App:010046")
````
Fortunately there is a shortcut for it:
Fortunately there is a shortcut way to code this:
````C#
throw new BusinessException("App:010046")
.WithData("UserName", "john");
````
Then the localized text can contain the`UserName` parameter:
Then the localized text can contain the`UserName` parameter:
````json
{
@ -263,7 +263,7 @@ Then the localized text can contain the`UserName` parameter:
}
````
* `WithData` can be chained for more than one parameter (like `.WithData(...).WithData(...)`).
* `WithData` can be chained with more than one parameter (like `.WithData(...).WithData(...)`).
### HTTP Status Code Mapping
@ -278,11 +278,11 @@ ABP tries to automatically determine the most suitable HTTP status code for comm
* Returns `501` (not implemented) for the `NotImplementedException`.
* Returns `500` (internal server error) for other exceptions (those are assumed as infrastructure exceptions).
The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. Default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended if needed.
The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. The default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended as needed.
#### Custom Mappings
Automatic HTTP status code determination can be overrided by custom mappings. Example:
Automatic HTTP status code determination can be overrided by custom mappings. For example:
Some exception types are automatically thrown by the framework.
Some exception types are automatically thrown by the framework:
- `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See authorization document (TODO: link) for more.
- `AbpValidationException` is thrown if the input of the current request is not valid. See validation document (TODO: link) for more.
- `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md).
You can also throw these type of exceptions in your code (while it's rarely needed).
You can also throw these type of exceptions in your code (although it's rarely needed).
ABP itself is a modular framework. It also provides infrastructure and architectural model to develop your own modules.
ABP is itself a modular framework. It also provides an infrastructure and architectural model to develop your own modules.
### Module Class
Every module should define a module class. Most simple way of defining a module class is to create a class derived from ``AbpModule`` as like below:
Every module should define a module class. The simplest way of defining a module class is to create a class derived from ``AbpModule`` as shown below:
````C#
public class BlogModule : AbpModule
@ -20,7 +20,7 @@ public class BlogModule : AbpModule
##### ConfigureServices Method
``ConfigureServices`` is the main method to add your services to dependency injection system and configure other modules. Example:
``ConfigureServices`` is the main method to add your services to the dependency injection system and configure other modules. Example:
````C#
public class BlogModule : AbpModule
@ -32,9 +32,9 @@ public class BlogModule : AbpModule
}
````
You can register dependencies one by one as stated in Microsoft's <ahref="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection"target="_blank">documentation</a>. Bu ABP has a **conventional dependency registration system** which automatically register all services in your assembly. See the [dependency Injection](Dependency-Injection.md) documentation for more about the dependency injection system.
You can register dependencies one by one as stated in Microsoft's <ahref="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection"target="_blank">documentation</a>. But ABP has a **conventional dependency registration system** which automatically register all services in your assembly. See the [dependency Injection](Dependency-Injection.md) documentation for more about the dependency injection system.
You can also configure other services and modules in this method. Example:
You can also configure other services and modules in this way. Example:
````C#
public class BlogModule : AbpModule
@ -54,11 +54,11 @@ See Configuration (TODO: link) document for more about the configuration system.
##### Pre & Post Configure Services
``AbpModule`` class also defines ``PreConfigureServices`` and ``PostConfigureServices`` methods to override and write your code just before and just after ``ConfigureServices``. Notice that the code you have written into these methods will be executed before/after ``ConfigureServices`` methods of all other modules.
``AbpModule`` class also defines ``PreConfigureServices`` and ``PostConfigureServices`` methods to override and write your code just before and just after ``ConfigureServices``. Notice that the code you have written into these methods will be executed before/after the ``ConfigureServices`` methods of all other modules.
#### Application Initialization
Once all services of all modules are configured, application starts by initializing all modules. In this phase, you can resolve services from ``IServiceProvider`` since it's ready and available.
Once all the services of all modules are configured, the application starts by initializing all modules. In this phase, you can resolve services from ``IServiceProvider`` since it's ready and available.
##### OnApplicationInitialization Method
@ -77,7 +77,7 @@ public class BlogModule : AbpModule
}
````
``OnApplicationInitialization`` is generally used by the startup module to construct middleware pipeline for ASP.NET Core applications. Example:
``OnApplicationInitialization`` is generally used by the startup module to construct the middleware pipeline for ASP.NET Core applications. Example:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
@ -100,19 +100,19 @@ public class AppModule : AbpModule
}
````
You can also perform startup logic if your module requires
You can also perform startup logic if your module requires it
##### Pre & Post Application Initialization
``AbpModule`` class also defines ``OnPreApplicationInitialization`` and ``OnPostApplicationInitialization`` methods to override and write your code just before and just after ``OnApplicationInitialization``. Notice that the code you have written into these methods will be executed before/after ``OnApplicationInitialization`` methods of all other modules.
``AbpModule`` class also defines ``OnPreApplicationInitialization`` and ``OnPostApplicationInitialization`` methods to override and write your code just before and just after ``OnApplicationInitialization``. Notice that the code you have written into these methods will be executed before/after the ``OnApplicationInitialization`` methods of all other modules.
#### Application Shutdown
Lastly, you can override ``OnApplicationShutdown`` method if you want to execute a code while application is beign shutdown.
Lastly, you can override ``OnApplicationShutdown`` method if you want to execute some code while application is beign shutdown.
### Module Dependencies
In a modular application, it's typical a module depends on other modules. An Abp module must declare ``[DependsOn]`` attribute if it depends on another module, as shown below:
In a modular application, it's not unusual for one module to depend upon another module(s). An Abp module must declare ``[DependsOn]`` attribute if it does have a dependcy upon another module, as shown below:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
@ -125,4 +125,4 @@ public class BlogModule
You can use multiple ``DependsOn`` attribute or pass multiple module types to a single ``DependsOn`` attribute depending on your preference.
A depended module may depend on another module, but you only need to define your direct dependencies. ABP investigates dependency graph on application startup and initializes/shutdowns modules in the correct order.
A depended module may depend on another module, but you only need to define your direct dependencies. ABP investigates the dependency graph for the application at startup and initializes/shutdowns modules in the correct order.
@ -308,7 +308,7 @@ Open the `Index.cshtml` and change the content as shown below:
<h2>Books</h2>
````
* This page **inherits** from the `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
* Change the default inhertitance of the Razor View Page Model so it **inherits** from the `BookStorePageBase` class (instead of `PageModel`). The `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
Virtual File System makes possible to manage files those are not physically exists in the file system (disk). It's mainly used to embed (js, css, image, cshtml...) files into assemblies and use them like physical files on runtime.
The Virtual File System makes it possible to manage files that do not physically exist on the file system (disk). It's mainly used to embed (js, css, image, cshtml...) files into assemblies and use them like physical files on runtime.
### Volo.Abp.VirtualFileSystem Package
@ -44,7 +44,7 @@ If you want to add multiple files, this can be tedious. Alternatively, you can d
This configuration recursively adds all files under the **MyResources** folder of the project (including the files you will add in the future).
Then the module should configure `VirtualFileSystemOptions` to register embedded files to the virtual file system. Example:
Then the module should be configured using`VirtualFileSystemOptions` to register the embedded files to the virtual file system. Example:
````C#
using Microsoft.Extensions.DependencyInjection;
@ -70,7 +70,7 @@ namespace MyCompany.MyProject
}
````
`AddEmbedded` extension method takes a class, finds all embedded files from the assembly of the given class and register to the virtual file system. It's a shortcut and could be written as shown below:
The `AddEmbedded` extension method takes a class, finds all embedded files from the assembly of the given class and registers them to the virtual file system. More concisely it could be written as shown below:
#### Dealing With Embedded Files During Development
Embedding a file into a module assembly and using it from another project by just referencing the assembly (or adding a nuget package) is very valuable for creating a re-usable module. However, it makes a bit hard to develop the module itself.
Embedding a file into a module assembly and using it from another project by just referencing the assembly (or adding a nuget package) is very valuable for creating a re-usable module. However, it makes it a bit hard to develop the module itself.
Assume that you are developing a module that contains an embedded JavaScript file. Whenever you change the file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, it is very time consuming and tedious.
What if the application could directly use the physical file in the development time? Thus, you could just refresh the browser page to see any change in the JavaScript file. `ReplaceEmbeddedByPyhsical` method makes that possible.
What is needed is the ability for the application to directly use the physical file at development time and a browser refresh shows any change in the JavaScript file. `ReplaceEmbeddedByPyhsical` method makes that possible.
The example below shows an application depends on a module (`MyModule`) that contains embedded files and the application can reach the source code of the module on the development time.
@ -162,4 +162,4 @@ Adding virtual files middleware after the static files middleware makes it possi
Embedded razor views/pages are available in the application without any configuration. Just place them into standard Views/Pages virtual folders in the module development.
An embedded view/page can be overrided if a module/application locate a new file into the same location.
An embedded view/page can be overrided if a module/application locates a new file into the same location.