@ -0,0 +1,258 @@ |
|||
# ABP.IO Platform 5.3 RC Has Been Released |
|||
|
|||
Today, we are happy to release the [ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) version **5.3 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. |
|||
|
|||
> **The planned release date for the [5.3.0 Stable](https://github.com/abpframework/abp/milestone/69) version is May 31, 2022**. |
|||
|
|||
Please try this version and provide feedback for a more stable ABP version 5.3! Thank you all. |
|||
|
|||
## Get Started with the 5.3 RC |
|||
|
|||
Follow the steps below to try version 5.3.0 RC today: |
|||
|
|||
1) **Upgrade** the ABP CLI to version `5.3.0-rc.1` using a command line terminal: |
|||
|
|||
````bash |
|||
dotnet tool update Volo.Abp.Cli -g --version 5.3.0-rc.1 |
|||
```` |
|||
|
|||
**or install** it if you haven't before: |
|||
|
|||
````bash |
|||
dotnet tool install Volo.Abp.Cli -g --version 5.3.0-rc.1 |
|||
```` |
|||
|
|||
2) Create a **new application** with the `--preview` option: |
|||
|
|||
````bash |
|||
abp new BookStore --preview |
|||
```` |
|||
|
|||
See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. |
|||
|
|||
> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**. |
|||
|
|||
You can use any IDE that supports .NET 6.x, like **[Visual Studio 2022](https://visualstudio.microsoft.com/downloads/)**. |
|||
|
|||
## Migration Notes |
|||
|
|||
There is a change in this version that may effect your applications: |
|||
|
|||
* Upgraded the [AutoMapper](https://github.com/AutoMapper/AutoMapper) library to **v11.0.1**. So, you need to change your project's target SDK that use the **AutoMapper** library (typically your `*.Application` project). You can change it from `netstandard2.0` to `netstandard2.1` or `net6` if needed. You can write to [#12189](https://github.com/abpframework/abp/pull/12189) if you need any help. |
|||
|
|||
## What's New with ABP Framework 5.3? |
|||
|
|||
In this section, I will introduce some major features released with this version. Here is a brief list of titles explained in the next sections: |
|||
|
|||
* Single-layer option added to the [*Get Started*](https://abp.io/get-started) page |
|||
* PWA Support for Startup Templates |
|||
* Introduced the `Volo.Abp.Gdpr.Abstractions` package |
|||
* Batch Publish Events from Outbox to the Event Bus |
|||
* Improvements on **eShopOnAbp** Project & E-Book Announcement |
|||
* LeptonX Lite Documentations & Project Status & Roadmap |
|||
* OpenIddict Module & Keycloack Integration |
|||
* Deployment Documentations |
|||
* Other News |
|||
|
|||
### Single-layer Option on *Get Started* Page |
|||
|
|||
We've created a new startup template named `app-nolayers` and [announced](https://blog.abp.io/abp/ABP.IO-Platform-5-2-RC-Has-Been-Published) it in the previous version. In this version, we've also added this startup template option to the *Get Started* page. |
|||
|
|||
*You can examine the screenshot below to see how to create an `app-nolayers` template from the ["Get Started"](https://abp.io/get-started) page:* |
|||
|
|||
 |
|||
|
|||
### PWA Support for Startup Templates |
|||
|
|||
ABP v5.3 application startup template now supports PWA for Blazor WASM & Angular UIs. To create a startup template with the PWA support, you can use the `--pwa` parameter. |
|||
|
|||
Example: |
|||
|
|||
```bash |
|||
abp new MyProgressiveWebApp -t app -u blazor --pwa |
|||
``` |
|||
|
|||
### Introducing the `Volo.Abp.Gdpr.Abstractions` Package |
|||
|
|||
A new `Volo.Abp.Gdpr.Abstractions` package has been added to the framework. This is an abstraction package, so doesn't contain any actual GDPR implementation. It defines some classes and interfaces to put a standard for who want to implement a GDPR module that can run in a modular or microservice system. |
|||
|
|||
At that point, we are introducing the **GDPR Module** for the ABP Commercial customers and this module does the GDPR-related operations on behalf of you, such as *"Download/Delete Personal Data"*. I'll describe the **GDPR Module** later in this blog post. |
|||
|
|||
> Please see the **GDPR Module** section below to learn more about this module. |
|||
|
|||
### Batch Publish Events from Outbox to the Event Bus |
|||
|
|||
We introduced the "Transactional Outbox & Inbox Patterns" in [**ABP v5.0**](https://blog.abp.io/abp/ABP-IO-Platform-5.0-RC-1-Has-Been-Released), it was one of the most awaited features by several software developers. |
|||
|
|||
We've made some optimizations for the **Batch Event Publishing** in this version, you can examine the related development from [here](https://github.com/abpframework/abp/pull/11243). After the optimization, the results are impressive. It is enabled by default (if you have configured [event outbox](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus#outbox-inbox-for-transactional-events)), so you don't need to any manual configuration. |
|||
|
|||
### Improvements on eShopOnAbp Project & E-Book Announcement |
|||
|
|||
There are some developments on the [eShopOnAbp project](https://github.com/abpframework/eShopOnAbp) made in this version. You can see the brief descriptions of some of the improvements below: |
|||
|
|||
* Local certificates have been created to use while working in Kubernetes and also Helm Charts have been updated. See [#107](https://github.com/abpframework/eShopOnAbp/pull/107). |
|||
* The Order Management page has been created. See [#92](https://github.com/abpframework/eShopOnAbp/pull/92). |
|||
* Database migration event handlers have been removed and "Distributed Locking" is now used for database migrations. See [#85](https://github.com/abpframework/eShopOnAbp/pull/85) and [#102](https://github.com/abpframework/eShopOnAbp/pull/102). |
|||
* Switched from Ocelot to YARP as the gateway. See [#97](https://github.com/abpframework/eShopOnAbp/pull/97). |
|||
|
|||
We have exciting news to share with the community, we're working on an "ABP Microservice Development" e-book. In this book, we're using the eShopOnAbp project as a reference microservice solution and we're trying to explain our experiences during the microservice application development process through this project. |
|||
|
|||
We're planning to create this book in nine chapters and make it available after the third chapter is written. After that, you will be able to download this free e-book from the [abp.io](https://abp.io/) website. |
|||
|
|||
### LeptonX Lite Documentations & Project Status & Roadmap |
|||
|
|||
It is finally here, we've released the **1.0.0-beta.1** version for the **LeptonX Lite**. |
|||
|
|||
 |
|||
|
|||
Lepton X Lite documents have been written for the three UI types within this version. You can see the related documentation from the screenshot above. You can follow these documents and try the new **LeptonX Lite Theme**. |
|||
|
|||
We don't suggest using the **beta.1** version on production, we highly demand you to test **LeptonX Lite** and provide feedback to us. It's really important for us to be able to release a more stable version. Thanks in advance. |
|||
|
|||
For the following versions (beta.2 and RC versions), we will focus on: |
|||
|
|||
* Fixing the reported bugs from the community |
|||
* Providing documentations as much as possible |
|||
* Adding new custom pages to the demo |
|||
|
|||
### OpenIddict Module & Keycloack Integration |
|||
|
|||
We have [announced the plan of replacing the IdentityServer](https://github.com/abpframework/abp/issues/11989). ABP currently uses **IdentityServer4** to add **OAuth** features as built-in on the server-side. However, since *IdentityServer4's support ends at the end of the year 2022*. Its replacement is Duende IdentityServer, which is not a free software anymore. (see [more](https://blog.duendesoftware.com/posts/20220111_fair_trade/)) |
|||
|
|||
Therefore, we've decided to completely drop the **IdentityServer4** from the ABP platform and implement the [OpenIddict](https://github.com/openiddict/openiddict-core) and install onto the startup templates. |
|||
|
|||
We've implemented both open source and commercial OpenIddict modules, we plan to remove Identity Server and replace it with OpenIddict for template projects in **ABP v5.4**. Please check [#12084](https://github.com/abpframework/abp/pull/12084) to see the development made on the open-source side. |
|||
|
|||
We're creating the documentation for the OpenIddict Module, if you want to have general knowledge about this module, you can check the documentation from [here](https://github.com/abpframework/abp/blob/dev/docs/en/Modules/OpenIddict.md). Currently, this is a draft documentation but it gives overall knowledge about the OpenIddict Module, we'll complete this documentation in ABP v5.4 and you'll be able to read it completely. |
|||
|
|||
Currently, we are also working on Keycloak integration possibilities in parallel to the OpenIddict integration research and we've prepared some samples that you can examine. You can see [#154](https://github.com/abpframework/abp-samples/pull/154) and [#158](https://github.com/abpframework/abp-samples/pull/158). |
|||
|
|||
### Deployment Documentations |
|||
|
|||
Deploying an ABP-based application is not so different than deploying any .NET or ASP.NET Core application. You can deploy it to a cloud provider (e.g. Azure, AWS, Google Could) or on-premise server, IIS or any other web server. However, we wanted to prepare a "Deployment Guide" to mention the important points and considerations. |
|||
|
|||
 |
|||
|
|||
In the [Deploying to a Clustered Environment](https://docs.abp.io/en/abp/5.3/Deployment/Clustered-Environment) documentation, we've documented the topics that you should consider when you are developing your application to a clustered environment and explained how you can deal with these topics in your ABP-based application. |
|||
|
|||
### Other News |
|||
|
|||
* Global Features were only accessible from the C# code. From this version and on, Global Features can be also provided from application configurations. See [#12043](https://github.com/abpframework/abp/pull/12043). |
|||
* Getting the user's detailed information (name, surname and phone number) from external login. See [#12085](https://github.com/abpframework/abp/pull/12085). |
|||
* Date Pipes for Angular. See [#11909](https://github.com/abpframework/abp/issues/11909). |
|||
|
|||
If you want to see more details, you can check [the release on GitHub](https://github.com/abpframework/abp/releases/tag/5.3.0-rc.1), which contains a list of all the issues and pull requests closed with this version. |
|||
|
|||
## What's New with ABP Commercial 5.3? |
|||
|
|||
### GDPR Module |
|||
|
|||
> **GDPR (General Data Protection Regulation)** is a regulation in EU law on data protection and known as the toughest privacy and security law in the world. GDPR applies to any organization operating within the EU, as well as any organizations outside of the EU which offer goods or services to customers or businesses in the EU. |
|||
|
|||
With this version, we are introducing the new **GDPR Module**. This was one of the most awaited features, so we've prioritized it and implemented it in this version. |
|||
|
|||
The GDPR Module is pre-installed in the [startup templates](https://docs.abp.io/en/commercial/5.3/startup-templates/index) for MVC. So, no need to manually install it. When you create a new startup template, you can directly use this module. We'll also implement this module for the other UI types as soon as possible and also add extra functionality such as "Cookie Consent" and more. |
|||
|
|||
Currently, there are two main functions of this module and they are "Download Personal Data" and "Delete Personal Data". |
|||
|
|||
 |
|||
|
|||
There is a "Personal Data" section in the user menu as in the screenshot above and when you click on this section, you'll be redirected to the "Personal Data" page. On that page, you can either request to "Download Personal Data" or "Delete Personal Data". |
|||
|
|||
 |
|||
|
|||
After you've requested to download "Personal Data", you need to wait for 1 hour by default (you can configure the related option). Because the GDPR module is developed by considering the distributed systems and therefore a specific time should be passed to ensure all the published events are handled and all personal data is collected. |
|||
|
|||
### CMS Kit Pro - Polling Feature |
|||
|
|||
We've added a **Polling** feature to the **CMS Kit Pro** module. This feature allows you to use a questionnaire/voting system in your application easily. You can create a question, define some options for it and the poll will be created for you. You can see the example poll in the screenshot below: |
|||
|
|||
 |
|||
|
|||
Also, there is an admin side of the Polling Feature. You can easily manage your polls in your admin (back-office) project. You can create, update, delete and show the results of the poll on the Polls page: |
|||
|
|||
 |
|||
|
|||
### OAuth Resource Owner Password as External Login Provider |
|||
|
|||
> The Resource Owner Password flow allows for the exchanging of the username and password of a user for an access token. When using the resource owner password credentials grant, the user provides the credentials (username and password) directly to the application. |
|||
|
|||
Now, you can login by entering a username and password from an OAuth server. |
|||
|
|||
Example: Use OAuth external login provider with Keycloak: |
|||
|
|||
 |
|||
|
|||
### Suite New Features & Enhancements |
|||
|
|||
In this version, there are some enhancements and new features in **Suite** and they are listed briefly below: |
|||
|
|||
* It's now possible to create an **app-nolayers (Application - single layer)** template via Suite and also code-generation is supported for the **app-nolayers** template with this version. |
|||
* Suite now allows users to see and download its logs. |
|||
* Suite now allows generating code via CLI. If you have a JSON file that contains code blocks, like entity configurations, you can use the `abp suite generate` command to generate CRUD pages based on it. |
|||
|
|||
Example: |
|||
|
|||
```bash |
|||
abp suite generate -e C:\Users\.suite\entities\Country.json -s C:\Users\my-proj\SuiteProj\SuiteProj.sln |
|||
``` |
|||
|
|||
### Suite Webinar: Take a closer look at the code generation |
|||
|
|||
 |
|||
|
|||
We've organized a webinar for Suite and in this webinar, we've talked about ABP Suite's capabilities, important features and more... |
|||
|
|||
You can watch the event from [here](https://www.youtube.com/watch?v=RFArBh60RSA&t=3s), if you haven't watched it yet. |
|||
|
|||
### Docker Compose Configurations for Single Layer Startup Template |
|||
|
|||
Dockerfiles, docker-compose files and build script files have been added to the Single Layer Startup Template (app-nolayers) with this version. |
|||
|
|||
And this way, applications created with this template now can be deployed more easily. |
|||
|
|||
### Microservice Solution Enhancements |
|||
|
|||
There are some enhancements made in the Microservice Solution. You can see the list of these enhancements: |
|||
|
|||
* Initial migration on the template has been updated with the small improvement that was made in the **Language Management** module. |
|||
* Database migration event handlers have been removed and "Distributed Locking" is now used for the database migrations. |
|||
|
|||
### PWA Support for the Application Pro Template |
|||
|
|||
Application Pro template also supports the PWA for Blazor WASM & Angular UIS. To create a startup template with the PWA support, you can use the `--pwa` parameter. |
|||
Example: |
|||
|
|||
```bash |
|||
abp new MyProgressiveWebApp -t app-pro -u blazor --pwa |
|||
``` |
|||
|
|||
## Community News |
|||
|
|||
### New ABP Community Posts |
|||
|
|||
* [Anto Subash](https://twitter.com/antosubash) created a series named ["Microservice with ABP"](https://blog.antosubash.com/posts/abp-microservice-series) and shared a couple of video posts about the ABP Microservice solution. |
|||
* [Francisco Kadzi](https://github.com/CiscoNinja) has created his first ABP Community article that shows how to ["Customize ABP Angular Application UI with AdminLTE"](https://community.abp.io/posts/customize-abp-angular-application-ui-with-adminlte.-7qu1m67s). |
|||
* [Jack Fistelmann](https://github.com/nebula2) has created an article to introduce a helpful project extension to speed up development on Visual Studio. You can read the article [here](https://community.abp.io/posts/using-switch-startup-project-extension-for-visual-studio-52yyw27v). |
|||
* [Jack Fistelmann](https://github.com/nebula2) has also created an article to show how you can generate PDF files with the `Sycyber.Core` package in ABP-based applications. You can read it [here](https://community.abp.io/posts/generate-pdfs-in-an-abp-framework-project-using-scryber.core-x9yh1vfa). |
|||
* [Halil Ibrahim Kalkan](https://twitter.com/hibrahimkalkan) has created an article to show ["Dealing with Multiple Implementations of a Service in ASP.NET Core & ABP Dependency Injection"](https://community.abp.io/posts/dealing-with-multiple-implementations-of-a-service-in-asp.net-core-abp-dependency-injection-ysfp4ho2) with examples. |
|||
* [Manoj Kumar](https://community.abp.io/members/manojkumar.t@shloklabs.com) submitted a new article about how to use "ABP authentication in a Flutter application". It was a frequently asked topic, which you can read [here](https://community.abp.io/posts/flutter-web-authentication-from-abp-mp6l2ehx). |
|||
* [Engincan Veske](https://twitter.com/EngincanVeske) created a new Community Article to show "Concurrency Check/Control in ABP". You can read it [here](https://community.abp.io/posts/handle-concurrency-with-ef-core-in-an-abp-framework-project-with-asp.net-core-mvc-jlkc3w8f). |
|||
|
|||
### ABP Community Talks 2022.4: How can you contribute to the open source ABP Framework? (May 10, 2022 - 17:00 UTC) |
|||
|
|||
 |
|||
|
|||
We've [asked you to pick the topic of the next Community Talks](https://twitter.com/abpframework/status/1514567683072745474?s=20&t=rJfHrB3DYDNsk2EXS8zBBQ) and you've chosen the "How to contribute to open source ABP Framework?" for the next talk topic. So, in this Community Talk, we will be talking about "How to contribute to ABP Framework" with one of the top contributors of the ABP Framework, [Ismail Yılmaz](https://github.com/iyilm4z). The event will be on **May 10, 2022, at 17:00 (UTC)** on YouTube. |
|||
|
|||
> You can register for the event from [here](https://kommunity.com/volosoft/events/abp-community-talks-20224-how-to-contribute-to-the-open-source-abp-framework-d9b50664), if you haven't registered yet. |
|||
|
|||
You can also [subscribe to the Volosoft channel](https://www.youtube.com/channel/UCO3XKlpvq8CA5MQNVS6b3dQ) to be informed about future ABP events and videos. |
|||
|
|||
### Discord Server |
|||
|
|||
We've created an official ABP Discord server so the ABP Community can interact with each other and created a blog-post to introduce it. You can read the [ABP Discord Server announcement post](https://blog.abp.io/abp/Official-ABP-Discord-Server-is-Here) to learn more about the ABP Discord Server. |
|||
|
|||
Thanks to the ABP Community, **700+** people joined our Discord Server so far and it grows every day. |
|||
|
|||
You can join our Discord Server from [here](https://discord.gg/abp), if you haven't yet. |
|||
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 371 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 58 KiB |
@ -0,0 +1,54 @@ |
|||
# How to contribute to abp.io as a frontend developer |
|||
|
|||
## How to setup development environment |
|||
|
|||
### Pre-requirements |
|||
|
|||
- Dotnet core SDK https://dotnet.microsoft.com/en-us/download |
|||
- Nodejs LTS https://nodejs.org/en/ |
|||
- Docker https://docs.docker.com/engine/install |
|||
- Angular CLI. https://angular.io/guide/what-is-angular#angular-cli |
|||
- Abp CLI https://docs.abp.io/en/abp/latest/cli |
|||
- A code editor |
|||
|
|||
Note: This arcticle prepare Windows OS. You may change the path type of your OS. an Example |
|||
|
|||
Windows: `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator\appsettings.json` |
|||
|
|||
Unix: `templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json` |
|||
|
|||
### Sample docker commands |
|||
|
|||
You need to install SQL Server and Redis. You can install these programs without docker, but my example uses docker containers. Your computer should have Docker Engine. Then open the terminal en execute the commands one by one. |
|||
For the Sql Server |
|||
|
|||
docker run -v sqlvolume:/var/opt/mssql -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourpassword -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-CU3-ubuntu-18.04 |
|||
|
|||
For the Redis |
|||
|
|||
docker run -p 6379:6379 -d redis |
|||
|
|||
Then we are ready to download and execute the code. |
|||
|
|||
## Folder Structure |
|||
|
|||
The app has a backend written in .net core (c#) and an angular app. It would help if you ran both of them. |
|||
|
|||
### Running Backend App |
|||
|
|||
The path of the Backend app is “templates\app\aspnet-core.” If you want to work with dockerized SQL Server, you should change connection strings for running with docker. The path of the connection string is |
|||
`templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator\appsettings.json`. |
|||
|
|||
Before running the backend, you should run the Db migrator project. The DbMigrator created initial tables and values. The path of DbMigrator is `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.DbMigrator`. Open a terminal in the path and execute the command `dotnet run` in terminal |
|||
|
|||
One last step before the running the backend is installing client-side libraries. Go to `templates\app\aspnet-core`. Open a terminal in the path and execute the command `abp install-libs` in terminal |
|||
|
|||
Next step you should go to path of backend host project. The path is `templates\app\aspnet-core\src\MyCompanyName.MyProjectName.HttpApi.HostWithIds`. Open a terminal in the path and execute the command `dotnet run` in terminal |
|||
|
|||
Your backend should be running successfully |
|||
|
|||
### Running Frontend App |
|||
|
|||
There is a demo app. The path of the demo app is `npm\ng-packs\apps\dev-app`. The demo app is connected to the packages with local references. Open the terminal in `npm\ng-packs\apps\dev-app` and execute `yarn` or `npm i` in terminal. After the package installed run `npm start` or `yarn start`. |
|||
|
|||
The repo uses Nx and packages connected with `local references`. The packages path is `”npm\ng-packs\packages` |
|||
@ -0,0 +1,11 @@ |
|||
# ABP Version 5.3 Migration Guide |
|||
|
|||
This document is a guide for upgrading ABP v5.2 solutions to ABP v5.3. There is a change in this version that may effect your applications, please read it carefully and apply the necessary changes to your application. |
|||
|
|||
## AutoMapper Upgraded to v11.0.1 |
|||
|
|||
AutoMapper library upgraded to **v11.0.1** in this version. So, you need to change your project's target SDK that use the **AutoMapper** library (typically your `*.Application` project). You can change it from `netstandard2.0` to `netstandard2.1` or `net6` if needed. Please see [#12189](https://github.com/abpframework/abp/pull/12189) for more info. |
|||
|
|||
## See Also |
|||
|
|||
* [Official blog post for the 5.3 release](https://blog.abp.io/abp/ABP.IO-Platform-5.3-RC-Has-Been-Published) |
|||
@ -0,0 +1,24 @@ |
|||
# ASP.NET Core MVC / Razor Pages UI: JavaScript Global Features API |
|||
|
|||
`abp.globalFeatures` API allows you to get the enabled features of the [Global Features](../../../Global-Features.md) in the client side. |
|||
|
|||
> This document only explains the JavaScript API. See the [Global Features](../../../Global-Features.md) document to understand the ABP Global Features system. |
|||
|
|||
## Usage |
|||
|
|||
````js |
|||
//Gets all enabled global features. |
|||
> abp.globalFeatures.enabledFeatures |
|||
|
|||
[ 'Shopping.Payment', 'Ecommerce.Subscription' ] |
|||
|
|||
|
|||
//Check the global feature is enabled |
|||
> abp.globalFeatures.isEnabled('Ecommerce.Subscription') |
|||
|
|||
true |
|||
|
|||
> abp.globalFeatures.isEnabled('My.Subscription') |
|||
|
|||
false |
|||
```` |
|||
@ -1,11 +1,28 @@ |
|||
# Blazor UI: Authentication |
|||
|
|||
The [application startup template](../../Startup-Templates/Application.md) is properly configured to use OpenId Connect to authenticate the user through the server side login form; |
|||
````json |
|||
//[doc-params] |
|||
{ |
|||
"UI": ["Blazor", "BlazorServer"] |
|||
} |
|||
```` |
|||
|
|||
The [application startup template](../../Startup-Templates/Application.md) is properly configured to use OpenId Connect to authenticate the user; |
|||
|
|||
{{if UI == "BlazorServer"}} |
|||
The Blazor Server application UI is actually a hybrid application that is combined with the MVC UI, and uses the login page provided by the MVC UI. When users enter a page that requires login, they are redirected to the `/Account/Login` page. Once they complete the login process, they are returned back to the application's UI. The login page also contains features like registration, password recovery, etc. |
|||
|
|||
{{end}} |
|||
|
|||
{{if UI == "Blazor"}} |
|||
* When the Blazor application needs to authenticate, it is redirected to the server side. |
|||
* Users can enter username & password to login if they already have an account. If not, they can use the register form to create a new user. They can also use forgot password and other features. The server side uses IdentityServer4 to handle the authentication. |
|||
* Users can enter username & password to login if they already have an account. If not, they can use the register form to create a new user. They can also use forgot password and other features., |
|||
|
|||
* Finally, they are redirected back to the Blazor application to complete the login process. |
|||
|
|||
This is a typical and recommended approach to implement authentication in Single-Page Applications. The client side configuration is done in the startup template, so you can change it. |
|||
|
|||
See the [Blazor Security document](https://docs.microsoft.com/en-us/aspnet/core/blazor/security) to understand and customize the authentication process. |
|||
|
|||
See the [Blazor Security document](https://docs.microsoft.com/en-us/aspnet/core/blazor/security) to understand and customize the authentication process. |
|||
|
|||
{{end}} |
|||
@ -0,0 +1,570 @@ |
|||
# Razor 集成 |
|||
|
|||
Razor模板是标准的C#类, 所以你可以使用任何C#的功能, 例如`依赖注入`, 使用`LINQ`, 自定义方法甚至使用`仓储` |
|||
|
|||
## 安装 |
|||
|
|||
建议使用[ABP CLI](CLI.md)安装此包. |
|||
|
|||
### 使用ABP CLI |
|||
|
|||
在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.TextTemplating.Razor |
|||
```` |
|||
|
|||
### 手动安装 |
|||
|
|||
如果你想要手动安装: |
|||
|
|||
1. 添加 [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet 包到你的项目: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.TextTemplating.Razor |
|||
```` |
|||
|
|||
2.添加 `AbpTextTemplatingRazorModule` 到你的模块的依赖列表: |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
//...other dependencies |
|||
typeof(AbpTextTemplatingRazorModule) //Add the new module dependency |
|||
)] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
## 添加 MetadataReference到CSharpCompilerOptions |
|||
|
|||
你需要将添加`MetadataReference`模板中使用的类型添加到 `CSharpCompilerOptions` 的 `References`. |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpRazorTemplateCSharpCompilerOptions>(options => |
|||
{ |
|||
options.References.Add(MetadataReference.CreateFromFile(typeof(YourModule).Assembly.Location)); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
## 添加MetadataReference到模板 |
|||
|
|||
你可以添加一些`MetadataReference`到模板 |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
services.Configure<AbpCompiledViewProviderOptions>(options => |
|||
{ |
|||
//Hello is template name. |
|||
options.TemplateReferences.Add("Hello", new List<Assembly>() |
|||
{ |
|||
Assembly.Load("Microsoft.Extensions.Logging.Abstractions"), |
|||
Assembly.Load("Microsoft.Extensions.Logging") |
|||
} |
|||
.Select(x => MetadataReference.CreateFromFile(x.Location)) |
|||
.ToList()); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
## 定义模板 |
|||
|
|||
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: |
|||
|
|||
````csharp |
|||
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider |
|||
{ |
|||
public override void Define(ITemplateDefinitionContext context) |
|||
{ |
|||
context.Add( |
|||
new TemplateDefinition("Hello") //template name: "Hello" |
|||
.WithRazorEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/Hello/Hello.cshtml", //template content path |
|||
isInlineLocalized: true |
|||
) |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. |
|||
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). |
|||
* `/Demos/Hello/Hello.cshtml` 是模板文件的路径. |
|||
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. |
|||
* `WithRenderEngine` 方法为模板设置渲染引擎. |
|||
|
|||
### 模板基类 |
|||
|
|||
每个 `cshtml` 模板页面都需要继承`RazorTemplatePageBase` 或 `RazorTemplatePageBase<Model>`. 基类提供了一些使用实用的属性可以在模板中使用. 例如: `Localizer`, `ServiceProvider`. |
|||
|
|||
### 模板内容 |
|||
|
|||
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.cshtml` 文件,并在属性窗口中将其标记为"**嵌入式资源**": |
|||
|
|||
 |
|||
|
|||
示例 `Hello.cshtml` 内容如下所示: |
|||
|
|||
```csharp |
|||
namespace HelloModelNamespace |
|||
{ |
|||
public class HelloModel |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: |
|||
|
|||
````csharp |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo"); |
|||
}); |
|||
```` |
|||
|
|||
* `TextTemplateDemoModule`是模块类. |
|||
* `TextTemplateDemo` 是你的项目的根命名空间. |
|||
|
|||
## 渲染模板 |
|||
|
|||
`ITemplateRenderer` 服务用于渲染模板内容. |
|||
|
|||
### 示例: 渲染一个简单的模板 |
|||
|
|||
````csharp |
|||
public class HelloDemo : ITransientDependency |
|||
{ |
|||
private readonly ITemplateRenderer _templateRenderer; |
|||
|
|||
public HelloDemo(ITemplateRenderer templateRenderer) |
|||
{ |
|||
_templateRenderer = templateRenderer; |
|||
} |
|||
|
|||
public async Task RunAsync() |
|||
{ |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"Hello", //the template name |
|||
new HelloModel |
|||
{ |
|||
Name = "John" |
|||
} |
|||
); |
|||
|
|||
Console.WriteLine(result); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. |
|||
* `RenderAsync` 有两个基本参数: |
|||
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). |
|||
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). |
|||
|
|||
示例会返回以下结果: |
|||
|
|||
````csharp |
|||
Hello John :) |
|||
```` |
|||
|
|||
## 本地化 |
|||
|
|||
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. |
|||
|
|||
### 内联本地化 |
|||
|
|||
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. |
|||
|
|||
#### 示例: 重置密码链接 |
|||
|
|||
假设你需要向用户发送电子邮件重置密码. 模板内容: |
|||
|
|||
```csharp |
|||
namespace ResetMyPasswordModelNamespace |
|||
{ |
|||
public class ResetMyPasswordModel |
|||
{ |
|||
public string Link { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```csharp |
|||
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<ResetMyPasswordModelNamespace.ResetMyPasswordModel> |
|||
<a title="@Localizer["ResetMyPasswordTitle"]" href="@Model.Link">@Localizer["ResetMyPassword", Model.Name]</a> |
|||
``` |
|||
|
|||
`Localizer` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: |
|||
|
|||
````json |
|||
"ResetMyPasswordTitle": "Reset my password", |
|||
"ResetMyPassword": "Hi {0}, Click here to reset your password" |
|||
```` |
|||
|
|||
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
"PasswordReset", //Template name |
|||
typeof(DemoResource) //LOCALIZATION RESOURCE |
|||
) |
|||
.WithRazorEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/PasswordReset/PasswordReset.cshtml", //template content path |
|||
isInlineLocalized: true |
|||
) |
|||
); |
|||
```` |
|||
|
|||
当你这样渲染模板时: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"PasswordReset", //the template name |
|||
new PasswordResetModel |
|||
{ |
|||
Name = "john", |
|||
Link = "https://abp.io/example-link?userId=123&token=ABC" |
|||
} |
|||
); |
|||
```` |
|||
|
|||
你可以看到以下本地化结果: |
|||
|
|||
````csharp |
|||
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a> |
|||
```` |
|||
|
|||
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. |
|||
|
|||
### 多个内容本地化 |
|||
|
|||
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. |
|||
|
|||
#### 示例: 欢迎电子邮件模板 |
|||
|
|||
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. |
|||
|
|||
首先创建一个文件夹,将模板放在里面,像 `en.cshtml`, `tr.cshtml` 每一个你支持的文化: |
|||
|
|||
 |
|||
|
|||
然后在模板定义提供程序类中添加模板定义: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
name: "WelcomeEmail", |
|||
defaultCultureName: "en" |
|||
) |
|||
.WithRazorEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/WelcomeEmail/Templates", //template content folder |
|||
isInlineLocalized: false |
|||
) |
|||
); |
|||
```` |
|||
|
|||
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. |
|||
* 指定 **模板文件夹** 而不是单个模板文件. |
|||
* 设置 `isInlineLocalized` 为 `false`. |
|||
|
|||
就这些,你可以渲染当前文化的模板: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync("WelcomeEmail"); |
|||
```` |
|||
|
|||
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. |
|||
|
|||
### 指定文化 |
|||
|
|||
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"WelcomeEmail", |
|||
cultureName: "en" |
|||
); |
|||
```` |
|||
|
|||
## 布局模板 |
|||
|
|||
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. |
|||
|
|||
### 示例: 邮件HTML布局模板 |
|||
|
|||
例如,你想为所有电子邮件模板创建一个布局. |
|||
|
|||
首先像之前一样创建一个模板文件: |
|||
|
|||
```csharp |
|||
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
@Body |
|||
</body> |
|||
</html> |
|||
``` |
|||
|
|||
* 布局模板必须具有 **Body** 部分作为渲染的子内容的占位符. |
|||
|
|||
在模板定义提供程序中注册模板: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
"EmailLayout", |
|||
isLayout: true //SET isLayout! |
|||
) |
|||
.WithRazorEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/EmailLayout/EmailLayout.cshtml", |
|||
isInlineLocalized: true |
|||
) |
|||
); |
|||
```` |
|||
|
|||
现在你可以将此模板用作任何其他模板的布局: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
name: "WelcomeEmail", |
|||
defaultCultureName: "en", |
|||
layout: "EmailLayout" //Set the LAYOUT |
|||
) |
|||
.WithRazorEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/WelcomeEmail/Templates", |
|||
isInlineLocalized: false |
|||
) |
|||
); |
|||
```` |
|||
|
|||
## 全局上下文 |
|||
|
|||
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. |
|||
|
|||
示例模板内容: |
|||
|
|||
````csharp |
|||
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase |
|||
A global object value: @GlobalContext["myGlobalObject"] |
|||
```` |
|||
|
|||
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"GlobalContextUsage", |
|||
globalContext: new Dictionary<string, object> |
|||
{ |
|||
{"myGlobalObject", "TEST VALUE"} |
|||
} |
|||
); |
|||
```` |
|||
|
|||
渲染的结果将是: |
|||
|
|||
```` |
|||
A global object value: TEST VALUE |
|||
```` |
|||
|
|||
## 替换存在的模板 |
|||
|
|||
通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码. |
|||
|
|||
### 选项-1: 使用虚拟文件系统 |
|||
|
|||
[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件. |
|||
|
|||
#### 示例: 替换标准电子邮件布局模板 |
|||
|
|||
ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.cshtml` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量. |
|||
|
|||
执行以下步骤将替换模板替换成你自定义的; |
|||
|
|||
**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.cshtml`): |
|||
|
|||
 |
|||
|
|||
**2)** 准备你的邮件布局模板: |
|||
|
|||
````html |
|||
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
<h1>This my header</h1> |
|||
|
|||
@Body |
|||
|
|||
<footer> |
|||
This is my footer... |
|||
</footer> |
|||
</body> |
|||
</html> |
|||
```` |
|||
|
|||
此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分). |
|||
|
|||
|
|||
**3)** 在`.csproj`文件配置嵌入式资源e |
|||
|
|||
* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目. |
|||
* 在 `.csproj` 中添加 `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>` 到 `<PropertyGroup>...</PropertyGroup>` 部分. |
|||
* 添加以下代码到你的 `.csproj` 文件: |
|||
|
|||
````xml |
|||
<ItemGroup> |
|||
<None Remove="Volo\Abp\Emailing\Templates\*.cshtml" /> |
|||
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\*.cshtml" /> |
|||
</ItemGroup> |
|||
```` |
|||
|
|||
这将模板文件做为"嵌入式资源". |
|||
|
|||
**4)** 配置虚拟文件系统 |
|||
|
|||
在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中: |
|||
|
|||
```csharp |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<BookStoreDomainModule>(); |
|||
}); |
|||
``` |
|||
|
|||
`BookStoreDomainModule` 应该是你的模块名称 |
|||
|
|||
> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件. |
|||
|
|||
现在渲染邮件布局模板时会使用你的模板. |
|||
|
|||
### 选项-2: 使用模板定义提供者 |
|||
|
|||
你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径. |
|||
|
|||
**示例: 使用 `/MyTemplates/EmailLayout.cshtml` 文件而不是标准模板** |
|||
|
|||
```csharp |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Emailing.Templates; |
|||
using Volo.Abp.TextTemplating; |
|||
|
|||
namespace MyProject |
|||
{ |
|||
public class MyTemplateDefinitionProvider |
|||
: TemplateDefinitionProvider, ITransientDependency |
|||
{ |
|||
public override void Define(ITemplateDefinitionContext context) |
|||
{ |
|||
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout); |
|||
|
|||
emailLayoutTemplate |
|||
.WithVirtualFilePath( |
|||
"/MyTemplates/EmailLayout.cshtml", |
|||
isInlineLocalized: true |
|||
); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
你依然应该添加 `/MyTemplates/EmailLayout.cshtml` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中. |
|||
|
|||
除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`. |
|||
|
|||
## 高级功能 |
|||
|
|||
本节介绍文本模板系统的一些内部知识和高级用法. |
|||
|
|||
### 模板内容Provider |
|||
|
|||
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. |
|||
|
|||
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. |
|||
|
|||
示例: |
|||
|
|||
````csharp |
|||
public class TemplateContentDemo : ITransientDependency |
|||
{ |
|||
private readonly ITemplateContentProvider _templateContentProvider; |
|||
|
|||
public TemplateContentDemo(ITemplateContentProvider templateContentProvider) |
|||
{ |
|||
_templateContentProvider = templateContentProvider; |
|||
} |
|||
|
|||
public async Task RunAsync() |
|||
{ |
|||
var result = await _templateContentProvider |
|||
.GetContentOrNullAsync("Hello"); |
|||
|
|||
Console.WriteLine(result); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
结果是原始模板内容: |
|||
|
|||
```` |
|||
@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<HelloModelNamespace.HelloModel> |
|||
Hello @Model.Name |
|||
```` |
|||
|
|||
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. |
|||
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). |
|||
|
|||
### 模板内容贡献者 |
|||
|
|||
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. |
|||
|
|||
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. |
|||
|
|||
示例: |
|||
|
|||
````csharp |
|||
public class MyTemplateContentProvider |
|||
: ITemplateContentContributor, ITransientDependency |
|||
{ |
|||
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context) |
|||
{ |
|||
var templateName = context.TemplateDefinition.Name; |
|||
|
|||
//TODO: Try to find content from another source |
|||
return null; |
|||
} |
|||
} |
|||
```` |
|||
|
|||
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. |
|||
|
|||
### Template Definition Manager |
|||
|
|||
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). |
|||
|
|||
## 另请参阅 |
|||
|
|||
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). |
|||
* [本地化系统](Localization.md). |
|||
* [虚拟文件系统](Virtual-File-System.md). |
|||
@ -0,0 +1,527 @@ |
|||
# Scriban 集成 |
|||
|
|||
## 安装 |
|||
|
|||
建议使用[ABP CLI](CLI.md)安装此包. |
|||
|
|||
### 使用ABP CLI |
|||
|
|||
在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.TextTemplating.Scriban |
|||
```` |
|||
|
|||
### 手动安装 |
|||
|
|||
如果你想要手动安装: |
|||
|
|||
1. 添加 [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet 包到你的项目: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.TextTemplating.Scriban |
|||
```` |
|||
|
|||
2.添加 `AbpTextTemplatingScribanModule` 到你的模块的依赖列表: |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
//...other dependencies |
|||
typeof(AbpTextTemplatingScribanModule) //Add the new module dependency |
|||
)] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
## 定义模板 |
|||
|
|||
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: |
|||
|
|||
````csharp |
|||
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider |
|||
{ |
|||
public override void Define(ITemplateDefinitionContext context) |
|||
{ |
|||
context.Add( |
|||
new TemplateDefinition("Hello") //template name: "Hello" |
|||
.WithVirtualFilePath( |
|||
"/Demos/Hello/Hello.tpl", //template content path |
|||
isInlineLocalized: true |
|||
) |
|||
.WithScribanEngine() |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. |
|||
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). |
|||
* `/Demos/Hello/Hello.tpl` 是模板文件的路径. |
|||
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. |
|||
* `WithScribanEngine` 方法为模板设置渲染引擎. |
|||
|
|||
### 模板内容 |
|||
|
|||
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**": |
|||
|
|||
 |
|||
|
|||
示例 `Hello.tpl` 内容如下所示: |
|||
|
|||
```` |
|||
Hello {%{{{model.name}}}%} :) |
|||
```` |
|||
|
|||
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: |
|||
|
|||
````csharp |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo"); |
|||
}); |
|||
```` |
|||
|
|||
* `TextTemplateDemoModule`是模块类. |
|||
* `TextTemplateDemo` 是你的项目的根命名空间. |
|||
|
|||
## 渲染模板 |
|||
|
|||
`ITemplateRenderer` 服务用于渲染模板内容. |
|||
|
|||
### 示例: 渲染一个简单的模板 |
|||
|
|||
````csharp |
|||
public class HelloDemo : ITransientDependency |
|||
{ |
|||
private readonly ITemplateRenderer _templateRenderer; |
|||
|
|||
public HelloDemo(ITemplateRenderer templateRenderer) |
|||
{ |
|||
_templateRenderer = templateRenderer; |
|||
} |
|||
|
|||
public async Task RunAsync() |
|||
{ |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"Hello", //the template name |
|||
new HelloModel |
|||
{ |
|||
Name = "John" |
|||
} |
|||
); |
|||
|
|||
Console.WriteLine(result); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. |
|||
* `RenderAsync` 有两个基本参数: |
|||
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). |
|||
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). |
|||
|
|||
示例会返回以下结果: |
|||
|
|||
````csharp |
|||
Hello John :) |
|||
```` |
|||
|
|||
### 匿名模型 |
|||
|
|||
虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"Hello", |
|||
new |
|||
{ |
|||
Name = "John" |
|||
} |
|||
); |
|||
```` |
|||
|
|||
示例中我们并没有创建模型类,但是创建了一个匿名对象模型. |
|||
|
|||
### PascalCase 与 snake_case |
|||
|
|||
PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`). |
|||
|
|||
## 本地化 |
|||
|
|||
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. |
|||
|
|||
### 内联本地化 |
|||
|
|||
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. |
|||
|
|||
#### 示例: 重置密码链接 |
|||
|
|||
假设你需要向用户发送电子邮件重置密码. 模板内容: |
|||
|
|||
```` |
|||
<a title="{%{{{L "ResetMyPasswordTitle"}}}%}" href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword" model.name}}}%}</a> |
|||
```` |
|||
|
|||
`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: |
|||
|
|||
````json |
|||
"ResetMyPasswordTitle": "Reset my password", |
|||
"ResetMyPassword": "Hi {0}, Click here to reset your password" |
|||
```` |
|||
|
|||
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
"PasswordReset", //Template name |
|||
typeof(DemoResource) //LOCALIZATION RESOURCE |
|||
) |
|||
.WithScribanEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/PasswordReset/PasswordReset.tpl", //template content path |
|||
isInlineLocalized: true |
|||
) |
|||
); |
|||
```` |
|||
|
|||
当你这样渲染模板时: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"PasswordReset", //the template name |
|||
new PasswordResetModel |
|||
{ |
|||
Name = "john", |
|||
Link = "https://abp.io/example-link?userId=123&token=ABC" |
|||
} |
|||
); |
|||
```` |
|||
|
|||
你可以看到以下本地化结果: |
|||
|
|||
````csharp |
|||
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a> |
|||
```` |
|||
|
|||
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. |
|||
|
|||
### 多个内容本地化 |
|||
|
|||
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. |
|||
|
|||
#### 示例: 欢迎电子邮件模板 |
|||
|
|||
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. |
|||
|
|||
首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化: |
|||
|
|||
 |
|||
|
|||
然后在模板定义提供程序类中添加模板定义: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
name: "WelcomeEmail", |
|||
defaultCultureName: "en" |
|||
) |
|||
.WithScribanEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/WelcomeEmail/Templates", //template content folder |
|||
isInlineLocalized: false |
|||
) |
|||
); |
|||
```` |
|||
|
|||
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. |
|||
* 指定 **模板文件夹** 而不是单个模板文件. |
|||
* 设置 `isInlineLocalized` 为 `false`. |
|||
|
|||
就这些,你可以渲染当前文化的模板: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync("WelcomeEmail"); |
|||
```` |
|||
|
|||
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. |
|||
|
|||
### 指定文化 |
|||
|
|||
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"WelcomeEmail", |
|||
cultureName: "en" |
|||
); |
|||
```` |
|||
|
|||
## 布局模板 |
|||
|
|||
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. |
|||
|
|||
### 示例: 邮件HTML布局模板 |
|||
|
|||
例如,你想为所有电子邮件模板创建一个布局. |
|||
|
|||
首先像之前一样创建一个模板文件: |
|||
|
|||
````xml |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
{%{{{content}}}%} |
|||
</body> |
|||
</html> |
|||
```` |
|||
|
|||
* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符. |
|||
|
|||
在模板定义提供程序中注册模板: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
"EmailLayout", |
|||
isLayout: true //SET isLayout! |
|||
) |
|||
.WithScribanEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/EmailLayout/EmailLayout.tpl", |
|||
isInlineLocalized: true |
|||
) |
|||
); |
|||
```` |
|||
|
|||
现在你可以将此模板用作任何其他模板的布局: |
|||
|
|||
````csharp |
|||
context.Add( |
|||
new TemplateDefinition( |
|||
name: "WelcomeEmail", |
|||
defaultCultureName: "en", |
|||
layout: "EmailLayout" //Set the LAYOUT |
|||
) |
|||
.WithScribanEngine() |
|||
.WithVirtualFilePath( |
|||
"/Demos/WelcomeEmail/Templates", |
|||
isInlineLocalized: false |
|||
) |
|||
); |
|||
```` |
|||
|
|||
## 全局上下文 |
|||
|
|||
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. |
|||
|
|||
示例模板内容: |
|||
|
|||
```` |
|||
A global object value: {%{{{myGlobalObject}}}%} |
|||
```` |
|||
|
|||
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: |
|||
|
|||
````csharp |
|||
var result = await _templateRenderer.RenderAsync( |
|||
"GlobalContextUsage", |
|||
globalContext: new Dictionary<string, object> |
|||
{ |
|||
{"myGlobalObject", "TEST VALUE"} |
|||
} |
|||
); |
|||
```` |
|||
|
|||
渲染的结果将是: |
|||
|
|||
```` |
|||
A global object value: TEST VALUE |
|||
```` |
|||
|
|||
## 替换存在的模板 |
|||
|
|||
通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码. |
|||
|
|||
### 选项-1: 使用虚拟文件系统 |
|||
|
|||
[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件. |
|||
|
|||
#### 示例: 替换标准电子邮件布局模板 |
|||
|
|||
ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.tp` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量. |
|||
|
|||
执行以下步骤将替换模板替换成你自定义的; |
|||
|
|||
**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.tpl`): |
|||
|
|||
 |
|||
|
|||
**2)** 准备你的邮件布局模板: |
|||
|
|||
````html |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
<h1>This my header</h1> |
|||
|
|||
{%{{{content}}}%} |
|||
|
|||
<footer> |
|||
This is my footer... |
|||
</footer> |
|||
</body> |
|||
</html> |
|||
```` |
|||
|
|||
此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分). |
|||
|
|||
|
|||
**3)** 在`.csproj`文件配置嵌入式资源e |
|||
|
|||
* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目. |
|||
* 在 `.csproj` 中添加 `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>` 到 `<PropertyGroup>...</PropertyGroup>` 部分. |
|||
* 添加以下代码到你的 `.csproj` 文件: |
|||
|
|||
````xml |
|||
<ItemGroup> |
|||
<None Remove="Volo\Abp\Emailing\Templates\*.tpl" /> |
|||
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\*.tpl" /> |
|||
</ItemGroup> |
|||
```` |
|||
|
|||
这将模板文件做为"嵌入式资源". |
|||
|
|||
**4)** 配置虚拟文件系统 |
|||
|
|||
在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中: |
|||
|
|||
```csharp |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<BookStoreDomainModule>(); |
|||
}); |
|||
``` |
|||
|
|||
`BookStoreDomainModule` 应该是你的模块名称 |
|||
|
|||
> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件. |
|||
|
|||
现在渲染邮件布局模板时会使用你的模板. |
|||
|
|||
### 选项-2: 使用模板定义提供者 |
|||
|
|||
你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径. |
|||
|
|||
**示例: 使用 `/MyTemplates/EmailLayout.tpl` 文件而不是标准模板** |
|||
|
|||
```csharp |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Emailing.Templates; |
|||
using Volo.Abp.TextTemplating; |
|||
|
|||
namespace MyProject |
|||
{ |
|||
public class MyTemplateDefinitionProvider |
|||
: TemplateDefinitionProvider, ITransientDependency |
|||
{ |
|||
public override void Define(ITemplateDefinitionContext context) |
|||
{ |
|||
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout); |
|||
|
|||
emailLayoutTemplate |
|||
.WithVirtualFilePath( |
|||
"/MyTemplates/EmailLayout.tpl", |
|||
isInlineLocalized: true |
|||
); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
你依然应该添加 `/MyTemplates/EmailLayout.tpl` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中. |
|||
|
|||
除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`. |
|||
|
|||
## 高级功能 |
|||
|
|||
本节介绍文本模板系统的一些内部知识和高级用法. |
|||
|
|||
### 模板内容Provider |
|||
|
|||
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. |
|||
|
|||
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. |
|||
|
|||
示例: |
|||
|
|||
````csharp |
|||
public class TemplateContentDemo : ITransientDependency |
|||
{ |
|||
private readonly ITemplateContentProvider _templateContentProvider; |
|||
|
|||
public TemplateContentDemo(ITemplateContentProvider templateContentProvider) |
|||
{ |
|||
_templateContentProvider = templateContentProvider; |
|||
} |
|||
|
|||
public async Task RunAsync() |
|||
{ |
|||
var result = await _templateContentProvider |
|||
.GetContentOrNullAsync("Hello"); |
|||
|
|||
Console.WriteLine(result); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
结果是原始模板内容: |
|||
|
|||
```` |
|||
Hello {%{{{model.name}}}%} :) |
|||
```` |
|||
|
|||
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. |
|||
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). |
|||
|
|||
### 模板内容贡献者 |
|||
|
|||
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. |
|||
|
|||
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. |
|||
|
|||
示例: |
|||
|
|||
````csharp |
|||
public class MyTemplateContentProvider |
|||
: ITemplateContentContributor, ITransientDependency |
|||
{ |
|||
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context) |
|||
{ |
|||
var templateName = context.TemplateDefinition.Name; |
|||
|
|||
//TODO: Try to find content from another source |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. |
|||
|
|||
### Template Definition Manager |
|||
|
|||
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). |
|||
|
|||
## 另请参阅 |
|||
|
|||
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). |
|||
* [本地化系统](Localization.md). |
|||
* [虚拟文件系统](Virtual-File-System.md). |
|||
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 7.3 KiB |