@ -1,5 +1,7 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"AbpTitle": "ABP Framework - Open Source Web Application Framework", |
|||
"AbpDescription": "ABP is an open source application framework focused on AspNet Core based web application development. Don't repeat yourself, focus on your own business code." |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"FAQ": "FAQ" |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "pt-BR", |
|||
"texts": { |
|||
"FAQ": "FAQ" |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "ro-RO", |
|||
"texts": { |
|||
"FAQ": "FAQ" |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "sl", |
|||
"texts": { |
|||
"FAQ": "FAQ" |
|||
} |
|||
} |
|||
|
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "tr", |
|||
"texts": { |
|||
"FAQ": "SSS" |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"FAQ": "常问问题" |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hant", |
|||
"texts": { |
|||
"FAQ": "常问问题" |
|||
} |
|||
} |
|||
@ -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,243 @@ |
|||
# Consuming HTTP APIs from a .NET Client Using ABP's Client Proxy System |
|||
|
|||
In this article, I will explain how to consume HTTP APIs from a .NET application using ABP's [dynamic](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients) and [static](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients) client-side proxy systems. I will start by creating a new project and consume the HTTP APIs from a .NET console application using dynamic client proxies. Then I will switch to static client proxies. Finally, I will glance at the differences and similarities between static and dynamic generic proxies. |
|||
|
|||
Here the main benefits of using the client-side proxy system (either dynamic or static): |
|||
|
|||
* Automatically maps C# method calls to remote server HTTP calls by considering the HTTP method, route, query string parameters, request payload and other details. |
|||
* Authenticates the HTTP Client by adding an access token to the HTTP header. |
|||
* Serializes to and deserialize from JSON. |
|||
* Handles HTTP API versioning. |
|||
* Adds correlation id, current tenant id and the current culture to the request. |
|||
* Properly handles the error messages sent by the server and throws proper exceptions. |
|||
|
|||
## Create a new ABP application with the ABP CLI |
|||
Firstly create a new solution via [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): |
|||
|
|||
```shell |
|||
abp new Acme.BookStore |
|||
``` |
|||
|
|||
> See ABP's [Getting Started document](https://docs.abp.io/en/abp/latest/Getting-Started-Setup-Environment?UI=MVC&DB=EF&Tiered=No) to learn how to create and run your application, if you haven't done it before. |
|||
|
|||
## Create the application service interface |
|||
I will start by creating an application service and exposing it as an HTTP API to be consumed by remote clients. First, define an interface for the application service; Create an `IBookAppService` interface in the `Books` folder (namespace) of the `Acme.BookStore.Application.Contracts` project: |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<PagedResultDto<BookDto>> GetListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Also add a `BookDto` class inside the same `Books` folder: |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public class BookDto |
|||
{ |
|||
public string Name { get; set; } |
|||
public string AuthorName { get; set; } |
|||
public float Price { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Implement the application service |
|||
It is time to implement the `IBookAppService` interface. Create a new class named `BookAppService` in the `Books` namespace (folder) of the `Acme.BookStore.Application` project: |
|||
|
|||
```csharp |
|||
using Acme.BookStore.Permissions; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public class BookAppService : ApplicationService, IBookAppService |
|||
{ |
|||
public Task<PagedResultDto<BookDto>> GetListAsync() |
|||
{ |
|||
var bookDtos = new List<BookDto>() |
|||
{ |
|||
new BookDto(){ Name = "Hunger", AuthorName ="Knut Hamsun", Price = 50}, |
|||
new BookDto(){ Name = "Crime and Punishment", AuthorName ="Dostoevsky", Price = 60}, |
|||
new BookDto(){ Name = "For Whom the Bell Tolls", AuthorName ="Ernest Hemingway", Price = 70} |
|||
}; |
|||
return Task.FromResult(new PagedResultDto<BookDto>( |
|||
bookDtos.Count, |
|||
bookDtos |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
It simply returns a list of books. You probably want to get the books from a database, but it doesn't matter for this article. If you want it, you can fully implement [this tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF). |
|||
|
|||
## Consume the app service from the console application |
|||
The startup solution comes with an example .NET console application (`Acme.BookStore.HttpApi.Client.ConsoleTestApp`) that is fully configured to consume your HTTP APIs remotely. Change `ClientDemoService` as shown in the following `Acme.BookStore.HttpApi.Client.ConsoleTestApp` project (it is under the `test` folder). |
|||
|
|||
```csharp |
|||
using Acme.BookStore.Books; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Acme.BookStore.HttpApi.Client.ConsoleTestApp; |
|||
|
|||
public class ClientDemoService : ITransientDependency |
|||
{ |
|||
private readonly IBookAppService _bookAppService; |
|||
|
|||
public ClientDemoService(IBookAppService bookAppService ) |
|||
{ |
|||
_bookAppService = bookAppService; |
|||
} |
|||
|
|||
public async Task RunAsync() |
|||
{ |
|||
var listOfBooks = await _bookAppService.GetListAsync(new PagedAndSortedResultRequestDto()); |
|||
Console.WriteLine($"Books: {string.Join(", ", listOfBooks.Items.Select(p => p.Name).ToList())}"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
We are basically injecting the `IBookAppService` interface to consume the remote service. ABP handles all the details (performing HTTP request, deserializing the resulting JSON object, etc) for us. |
|||
|
|||
You can run the application to see the output: |
|||
|
|||
``` |
|||
Books: Hunger, Crime and Punishment, For Whom the Bell Tolls |
|||
``` |
|||
|
|||
## Convert the application to use static client proxies |
|||
The [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application) comes pre-configured for the **dynamic** client proxy generation, in the `HttpApi.Client` project. If you want to switch to the **static** client proxies, you should change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project: |
|||
|
|||
```csharp |
|||
public class BookStoreHttpApiClientModule : AbpModule |
|||
{ |
|||
public const string RemoteServiceName = "Default"; |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Other configurations... |
|||
|
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly, |
|||
RemoteServiceName |
|||
); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The `AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation. |
|||
|
|||
|
|||
Now you're ready to generate the client proxy code by running the following command in the root folder of your client project **while your server-side project is running**: |
|||
|
|||
````bash |
|||
abp generate-proxy -t csharp -u http://localhost:44397/ |
|||
```` |
|||
|
|||
> The URL (`-u` parameter's value) might be different for your application. It should be the server's root URL. |
|||
|
|||
You should see the generated files under the selected folder: |
|||
|
|||
 |
|||
|
|||
Now you can run the console client application again. You should see the same output: |
|||
|
|||
```` |
|||
Books: Hunger, Crime and Punishment, For Whom the Bell Tolls |
|||
```` |
|||
|
|||
## Add authorization |
|||
The ABP Framework provides an [authorization system](https://docs.abp.io/en/abp/latest/Authorization) based on [ASP.NET Core's authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). We can define permissions and restrict access to some of our application's functionalities, so only the allowed users/clients can use these functionalities. Here, I will define a permission to be able to get the list of books. |
|||
|
|||
### Defining a permission |
|||
|
|||
Under `Acme.BookStore.Application.Contracts` open `BookStorePermissions` and paste the below code: |
|||
```csharp |
|||
namespace Acme.BookStore.Permissions; |
|||
|
|||
public static class BookStorePermissions |
|||
{ |
|||
public const string GroupName = "BookStore"; |
|||
|
|||
public static class Books |
|||
{ |
|||
public const string Default = GroupName + ".Books"; |
|||
} |
|||
|
|||
} |
|||
``` |
|||
You also need to change `BookStorePermissionDefinitionProvider` under the same folder and project as follows: |
|||
```csharp |
|||
using Acme.BookStore.Localization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.Localization; |
|||
|
|||
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName); |
|||
bookStoreGroup.AddPermission(BookStorePermissions.Books.Default); |
|||
} |
|||
} |
|||
``` |
|||
### Authorizing the application service |
|||
|
|||
We can now add the `[Authorize(BookStorePermissions.Books.Default)]` attribute to the `BookAppService` class: |
|||
|
|||
```csharp |
|||
[Authorize(BookStorePermissions.Books.Default)] |
|||
public class BookAppService : ApplicationService, IBookAppService |
|||
{ |
|||
... |
|||
} |
|||
``` |
|||
|
|||
If you run the server now, then run the console client application, you will see the following error on the console application: |
|||
|
|||
``` |
|||
Unhandled exception. Volo.Abp.Http.Client.AbpRemoteCallException: Forbidden at |
|||
Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1 |
|||
.ThrowExceptionForResponseAsync(HttpResponseMessage response)... |
|||
``` |
|||
|
|||
To fix the problem, we should grant permission to the admin user. We are granting permission to the admin user because the console application is configured to use the Resource Owner Password Grant Flow. That means the client application is consuming services on behalf of the admin user. You can see the configuration in the `appsettings.json` file of the console application. |
|||
|
|||
### Granting the permission |
|||
|
|||
Once you define the permissions, you can see them on the permission management modal. |
|||
|
|||
Go to the Administration -> Identity -> Roles page, select the Permissions action for the admin role to open the permission management modal: |
|||
 |
|||
Grant the permissions you want and save the modal. |
|||
|
|||
## Dynamic vs static proxies |
|||
|
|||
Static generic proxies provide **better performance** because they don't need to run on runtime, but you should **re-generate** them once you change the API endpoint definition. Dynamic generic proxies don't need to be re-generated because they work on the runtime but they have a slight performance penalty. |
|||
|
|||
## Further Reading |
|||
In this tutorial, I explained how you can create an example project and apply a static client proxy instead of a dynamic client proxy. I also summarized the differences between both approaches. If you want to get more information, you can read the following documents: |
|||
|
|||
* [Static C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients) |
|||
* [Dynamic C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients) |
|||
* [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF) |
|||
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 19 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,215 @@ |
|||
## ABP OpenIddict Modules |
|||
|
|||
## How to Install |
|||
|
|||
TODO: |
|||
|
|||
## User Interface |
|||
|
|||
This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module. |
|||
|
|||
## Relations to Other Modules |
|||
|
|||
This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md). |
|||
|
|||
## The module |
|||
|
|||
### Demo projects |
|||
|
|||
In the module's `app` directory there are six projects(including `angular`) |
|||
|
|||
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`). |
|||
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication |
|||
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication |
|||
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API` |
|||
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication |
|||
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication |
|||
|
|||
#### How to run? |
|||
|
|||
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data. |
|||
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test. |
|||
|
|||
### Domain module |
|||
|
|||
There are four main entities included in this module. |
|||
|
|||
* OpenIddictApplication: **Represents applications(client)** |
|||
* OpenIddictScope: **Represents scopes** |
|||
* OpenIddictAuthorization: **Represents authorizations, Track of logical chains of tokens and user consent..** |
|||
* OpenIddictToken: **Represents various tokens.** |
|||
|
|||
Domain also implements four store interfaces in OpenIddict, OpenIddict uses store to manage entities, corresponding to the above four entities, Custom entity repository is used in the store. |
|||
|
|||
|
|||
```cs |
|||
//Manager |
|||
OpenIddictApplicationManager |
|||
OpenIddictScopeManager |
|||
OpenIddictAuthorizationManager |
|||
OpenIddictTokenManager |
|||
|
|||
//Store |
|||
IOpenIddictApplicationStore |
|||
IOpenIddictScopeStore |
|||
IOpenIddictAuthorizationStore |
|||
IOpenIddictTokenStore |
|||
|
|||
//Repository |
|||
IOpenIddictApplicationRepository |
|||
IOpenIddictScopeRepository |
|||
IOpenIddictAuthorizationRepository |
|||
IOpenIddictTokenRepository |
|||
``` |
|||
|
|||
We enabled most of OpenIddict's features in the `AddOpenIddict` method, You can change OpenIddict's related builder options via `PreConfigure`. |
|||
|
|||
```cs |
|||
PreConfigure<OpenIddictBuilder>(builder => |
|||
{ |
|||
//builder |
|||
}); |
|||
|
|||
PreConfigure<OpenIddictCoreBuilder>(builder => |
|||
{ |
|||
//builder |
|||
}); |
|||
|
|||
PreConfigure<OpenIddictServerBuilder>(builder => |
|||
{ |
|||
//builder |
|||
}); |
|||
``` |
|||
|
|||
#### AbpOpenIddictAspNetCoreOptions |
|||
|
|||
`UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims. |
|||
`AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate. |
|||
|
|||
You can also change this options via `PreConfigure`. |
|||
|
|||
#### Automatically removing orphaned tokens/authorizations |
|||
|
|||
There is a background task in the `Domain` module (`enabled by default`) that automatically removes orphaned tokens/authorizations, you can configure `TokenCleanupOptions` to manage it. |
|||
|
|||
### ASP NET Core module |
|||
|
|||
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode). |
|||
|
|||
```cs |
|||
AuthorizeController -> connect/authorize |
|||
TokenController -> connect/token |
|||
LogoutController -> connect/logout |
|||
UserInfoController -> connect/userinfo |
|||
``` |
|||
|
|||
> We will implement the related functions of **device flow** in the PRO module.. |
|||
|
|||
#### How to control claims in access_token and id_token |
|||
|
|||
You can use the [Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) to add/remove claims to the `ClaimsPrincipal`. |
|||
|
|||
The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`. |
|||
|
|||
You can create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims |
|||
|
|||
```cs |
|||
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency |
|||
{ |
|||
public virtual Task SetDestinationsAsync(AbpOpenIddictClaimDestinationsProviderContext context) |
|||
{ |
|||
// ... |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
|
|||
Configure<AbpOpenIddictClaimDestinationsOptions>(options => |
|||
{ |
|||
options.ClaimDestinationsProvider.Add<MyClaimDestinationsProvider>(); |
|||
}); |
|||
``` |
|||
|
|||
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html) |
|||
|
|||
### EF Core module |
|||
|
|||
Implements the above four repository interfaces. |
|||
|
|||
### MongoDB module |
|||
|
|||
Implements the above four repository interfaces. |
|||
|
|||
|
|||
## OpenIddict |
|||
|
|||
### Documentation |
|||
|
|||
For more details about OpenIddict, please refer to its official documentation and Github. |
|||
|
|||
https://documentation.openiddict.com |
|||
|
|||
https://github.com/openiddict/openiddict-core#resources |
|||
|
|||
### Disable AccessToken Encryption |
|||
|
|||
ABP disables the `access token encryption` by default for compatibility, you can manually enable it if needed. |
|||
|
|||
```cs |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<OpenIddictServerBuilder>(builder => |
|||
{ |
|||
builder.Configure(options => options.DisableAccessTokenEncryption = false); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption |
|||
|
|||
|
|||
### PKCE |
|||
|
|||
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html |
|||
|
|||
### Request/Response process |
|||
|
|||
I will briefly introduce the principle of OpenIddict so that everyone can quickly understand it. |
|||
|
|||
The `OpenIddict.Server.AspNetCore` adds an authentication scheme(`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) and implements the `IAuthenticationRequestHandler` interface. |
|||
|
|||
It will be executed first in `AuthenticationMiddleware` and can short-circuit the current request. Otherwise, `DefaultAuthenticateScheme` will be called and continue to execute the pipeline. |
|||
|
|||
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers(Handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it. |
|||
|
|||
Example a token request: |
|||
|
|||
``` |
|||
POST /connect/token HTTP/1.1 |
|||
Content-Type: application/x-www-form-urlencoded |
|||
|
|||
grant_type=password& |
|||
client_id=AbpApp& |
|||
client_secret=1q2w3e*& |
|||
username=admin& |
|||
password=1q2w3E*& |
|||
scope=AbpAPI offline_access |
|||
``` |
|||
|
|||
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `http/https`, verify that the request parameters (`client. scope etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request. |
|||
|
|||
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the http request at this time. The rest of our work will be based on this object. |
|||
|
|||
We may check the `username` and `password` in the request. If it is correct we create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing. |
|||
|
|||
`OpenIddictServerAspNetCoreHandler` do some checks to generate json and replace the http response content. |
|||
|
|||
The `ForbidResult` `ChallengeResult` are all the above types of processing. |
|||
|
|||
If you need to customize OpenIddict, you need to replace/delete/add new handlers and make it execute in the correct order. |
|||
|
|||
Please refer to: |
|||
https://documentation.openiddict.com/guides/index.html#events-model |
|||
|
|||
## Sponsor |
|||
|
|||
Please consider sponsoring this project: https://github.com/sponsors/kevinchalet |
|||
@ -0,0 +1,107 @@ |
|||
# Application (Single Layer) Startup Template |
|||
|
|||
## Introduction |
|||
|
|||
This template provides a simple solution structure with a single project. This document explains that solution structure in details. |
|||
|
|||
### The Difference Between the Application Startup Templates |
|||
|
|||
ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. However, some developers find this template a little bit complex for simple and short-term applications. The single-layer application template has been created to provide a simpler development model for such applications. This template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) but the development model is minimal and everything is in a single project (`.csproj`). |
|||
|
|||
## How to Start with It? |
|||
|
|||
You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download this startup template from the [Get Started](https://abp.io/get-started) page. In this section, we will use the ABP CLI. |
|||
|
|||
Firstly, install the ABP CLI if you haven't installed it before: |
|||
|
|||
```bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
``` |
|||
|
|||
Then, use the `abp new` command in an empty folder to create a new solution: |
|||
|
|||
```bash |
|||
abp new Acme.BookStore -t app-nolayers |
|||
``` |
|||
|
|||
* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single-level, two-level or three-level naming. |
|||
* In this example, the `-t` (or `--template`) option specifies the template name. |
|||
|
|||
### Specify the UI Framework |
|||
|
|||
This template provides multiple UI frameworks: |
|||
|
|||
* `mvc`: ASP.NET Core MVC UI with Razor Pages (default) |
|||
* `blazor-server`: Blazor Server UI |
|||
* `angular`: Angular UI |
|||
* `none`: Without UI (for HTTP API development) |
|||
|
|||
> This template doesn't have Blazor WebAssembly UI, because it requires 3 projects at least (server-side, UI and shared library between these two projects). We are recommending to use the layered [application startup template](Application.md) for Blazor WebAssembly projects. |
|||
|
|||
Use the `-u` (or `--ui`) option to specify the UI framework while creating the solution: |
|||
|
|||
```bash |
|||
abp new Acme.BookStore -t app-nolayers -u angular |
|||
``` |
|||
|
|||
This example specifies the UI type (the `-u` option) as `angular`. You can also specify `mvc`, `blazor-server` or `none` for the UI type. |
|||
|
|||
### Specify the Database Provider |
|||
|
|||
This template supports the following database providers: |
|||
|
|||
- `ef`: Entity Framework Core (default) |
|||
- `mongodb`: MongoDB |
|||
|
|||
Use the `-d` (or `--database-provider`) option to specify the database provider while creating the solution: |
|||
|
|||
```bash |
|||
abp new Acme.BookStore -t app-nolayers -d mongodb |
|||
``` |
|||
|
|||
## Solution Structure |
|||
|
|||
If you don't specify any additional options while creating an `app-nolayers` template, you will have a solution as shown below: |
|||
|
|||
 |
|||
|
|||
In the next sections, we will explain the structure based on this example. Your startup solution can be slightly different based on your preferences. |
|||
|
|||
### Folder Structure |
|||
|
|||
Since this template provides a single-project solution, we've separated concerns into folders instead of projects. You can see the pre-defined folders as shown below: |
|||
|
|||
 |
|||
|
|||
* Define your database mappings (for [EF Core](../Entity-Framework-Core.md) or [MongoDB](../MongoDB.md)) and [repositories](../Repositories.md) in the `Data` folder. |
|||
* Define your [entities](../Entities.md) in the `Entities` folder. |
|||
* Define your UI localization keys/values in the `Localization` folder. |
|||
* Define your UI menu items in the `Menus` folder. |
|||
* Define your [object-to-object mapping](../Object-To-Object-Mapping.md) classes in the `ObjectMapping` folder. |
|||
* Define your UI pages (Razor Pages) in the `Pages` folder (create `Controllers` and `Views` folder yourself if you prefer the MVC pattern). |
|||
* Define your [application services](../Application-Services.md) in the `Services` folder. |
|||
|
|||
### How to Run? |
|||
|
|||
Before running the application, you need to create the database and seed the initial data. To do that, you can run the following command in the directory of your project (in the same folder of the `.csproj` file): |
|||
|
|||
```bash |
|||
dotnet run --migrate-database |
|||
``` |
|||
|
|||
This command will create the database and seed the initial data for you. Then you can run the application with any IDE that supports .NET or by running the `dotnet run` command in the directory of your project. The default username is `admin` and the password is `1q2w3E*`. |
|||
|
|||
> While creating a database & applying migrations seem only necessary for relational databases, you should run this command even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds the initial data which is necessary for the application. |
|||
|
|||
### The Angular UI |
|||
|
|||
If you choose `Angular` as the UI framework, the solution will be separated into two folders: |
|||
|
|||
* An `angular` folder that contains the Angular UI application, the client-side code. |
|||
* An `aspnet-core` folder that contains the ASP.NET Core solution (a single project), the server-side code. |
|||
|
|||
The server-side is similar to the solution described in the *Solution Structure* section above. This project serves the API, so the Angular application can consume it. |
|||
|
|||
The client-side application consumes the HTTP APIs as mentioned. You can see the folder structure of the Angular project shown below: |
|||
|
|||
 |
|||
@ -0,0 +1,14 @@ |
|||
# Quick Start: Overall |
|||
|
|||
**Welcome to the ABP Framework**. This is a single-part, quick-start tutorial to build a simple application. Start with this tutorial if you want to quickly understand how ABP Framework works. |
|||
|
|||
## Select the Solution Architecture |
|||
|
|||
This tutorial has multiple versions. Please select the one best fits for you: |
|||
|
|||
* **[Single-Layer Solution](Single-Layer/Index.md)**: Creates a single-project solution. Recommended for building an application with a **simpler and easy to understand** architecture. |
|||
* **[Layered Solution Architecture](Index.md)**: A fully layered (multiple projects) solution based on [Domain Driven Design](../../Domain-Driven-Design.md) practices. Recommended for long-term projects that need a **maintainable and extensible** codebase. |
|||
|
|||
## See Also |
|||
|
|||
* Check the [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture. |
|||
@ -0,0 +1,755 @@ |
|||
# Quick Start |
|||
|
|||
````json |
|||
//[doc-params] |
|||
{ |
|||
"UI": ["MVC", "BlazorServer", "NG"], |
|||
"DB": ["EF", "Mongo"] |
|||
} |
|||
```` |
|||
|
|||
This is a single-part quick-start tutorial to build a simple todo application with the ABP Framework. Here's a screenshot from the final application: |
|||
|
|||
 |
|||
|
|||
You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). |
|||
|
|||
## Pre-Requirements |
|||
|
|||
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. |
|||
* [Node v14.x](https://nodejs.org/) |
|||
|
|||
{{if DB=="Mongo"}} |
|||
|
|||
* [MongoDB Server 4.0+](https://docs.mongodb.com/manual/administration/install-community/) |
|||
|
|||
{{end}} |
|||
|
|||
## Creating a New Solution |
|||
|
|||
In this tutorial, we will use the [ABP CLI](../../../CLI.md) to create the sample application with the ABP Framework. You can run the following command in a command-line terminal to install the **ABP CLI**, if you haven't installed it yet: |
|||
|
|||
````bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
Then create an empty folder, open a command-line terminal and execute the following command in the terminal: |
|||
|
|||
````bash |
|||
abp new TodoApp -t app-nolayers{{if UI=="BlazorServer"}} -u blazor-server{{else if UI=="NG"}} -u angular{{end}}{{if DB=="Mongo"}} -d mongodb{{end}} |
|||
```` |
|||
|
|||
{{if UI=="NG"}} |
|||
|
|||
This will create a new solution, named *TodoApp*, with `angular` and `aspnet-core` folders. Once the solution is ready, open the solution (in the `aspnet-core` folder) with your favorite IDE. |
|||
|
|||
{{else}} |
|||
|
|||
This will create a new solution with a single project, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. |
|||
|
|||
{{end}} |
|||
|
|||
### Create the Database |
|||
|
|||
You can run the following command in the root directory of your project (in the same folder of the `.csproj` file) to create the database and seed the initial data: |
|||
|
|||
```bash |
|||
dotnet run --migrate-database |
|||
``` |
|||
|
|||
This command will create the database and seed the initial data for you. Then you can run the application. |
|||
|
|||
### Run the Application |
|||
|
|||
{{if UI=="MVC" || UI=="BlazorServer"}} |
|||
|
|||
It is good to run the application before starting the development. Running the application is pretty straight-forward, you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project: |
|||
|
|||
{{else if UI=="NG"}} |
|||
|
|||
It is good to run the application before starting the development. The solution has two main applications: |
|||
|
|||
* `TodoApp` (in the .NET solution) hosts the server-side HTTP API, so the Angular application can consume it. (server-side application) |
|||
* `angular` folder contains the Angular application. (client-side application) |
|||
|
|||
Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on [Swagger UI](https://swagger.io/tools/swagger-ui/): |
|||
|
|||
 |
|||
|
|||
You can explore and test your HTTP API with this UI. If it works, then we can run the Angular client application. |
|||
|
|||
You can run the application using the following (or `yarn start`) command: |
|||
|
|||
````bash |
|||
npm start |
|||
```` |
|||
|
|||
This command takes time, but eventually runs and opens the application in your default browser: |
|||
|
|||
{{end}} |
|||
|
|||
 |
|||
|
|||
You can click on the *Login* button and use `admin` as the username and `1q2w3E*` as the password to login to the application. |
|||
|
|||
All right. We can start coding! |
|||
|
|||
## Defining the Entity |
|||
|
|||
This application will have a single [entity](../../../Entities.md) and we can start by creating it. So, create a new `TodoItem` class under the `Entities` folder of the project: |
|||
|
|||
````csharp |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace TodoApp.Entities; |
|||
|
|||
public class TodoItem : BasicAggregateRoot<Guid> |
|||
{ |
|||
public string Text { get; set; } |
|||
} |
|||
```` |
|||
|
|||
`BasicAggregateRoot` is the simplest base class to create root entities, and `Guid` is the primary key (`Id`) of the entity here. |
|||
|
|||
## Database Integration |
|||
|
|||
{{if DB=="EF"}} |
|||
|
|||
Next step is to setup the [Entity Framework Core](../../../Entity-Framework-Core.md) configuration. |
|||
|
|||
### Mapping Configuration |
|||
|
|||
Open the `TodoAppDbContext` class (in the `Data` folder) and add a new `DbSet` property to this class: |
|||
|
|||
````csharp |
|||
public DbSet<TodoItem> TodoItems { get; set; } |
|||
```` |
|||
|
|||
Then navigate to the `OnModelCreating` method in the same class and add the following mapping code for the `TodoItem ` entity: |
|||
|
|||
````csharp |
|||
protected override void OnModelCreating(ModelBuilder builder) |
|||
{ |
|||
base.OnModelCreating(builder); |
|||
|
|||
/* Include modules to your migration db context */ |
|||
|
|||
builder.ConfigurePermissionManagement(); |
|||
... |
|||
|
|||
/* Configure your own tables/entities inside here */ |
|||
builder.Entity<TodoItem>(b => |
|||
{ |
|||
b.ToTable("TodoItems"); |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is to create a migration and apply the changes to the database. |
|||
|
|||
### Code First Migrations |
|||
|
|||
The startup solution is configured to use Entity Framework Core [Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations). Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database. |
|||
|
|||
Open a command-line terminal in the root directory of your project and type the following command: |
|||
|
|||
````bash |
|||
dotnet ef migrations add Added_TodoItem |
|||
```` |
|||
|
|||
This will add a new migration class to the project. You should see the new migration in the `Migrations` folder: |
|||
|
|||
 |
|||
|
|||
Then, you can apply changes to the database using the following command, in the same command-line terminal: |
|||
|
|||
````bash |
|||
dotnet ef database update |
|||
```` |
|||
|
|||
{{else if DB=="Mongo"}} |
|||
|
|||
The next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppDbContext` class (under the **Data** folder) in your project and make the following changes: |
|||
|
|||
1. Add a new property to the class: |
|||
|
|||
````csharp |
|||
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>(); |
|||
```` |
|||
|
|||
2. Add the following code inside the `CreateModel` method: |
|||
|
|||
````csharp |
|||
modelBuilder.Entity<TodoItem>(b => |
|||
{ |
|||
b.CollectionName = "TodoItems"; |
|||
}); |
|||
```` |
|||
|
|||
{{end}} |
|||
|
|||
After the database integrations, now we can start to create application service methods and implement our use-cases. |
|||
|
|||
## Creating the Application Service |
|||
|
|||
An [application service](../../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases in this application: |
|||
|
|||
* Get the list of the todo items |
|||
* Create a new todo item |
|||
* Delete an existing todo item |
|||
|
|||
Before starting to implement these use cases, first we need to create a DTO class that will be used in the application service. |
|||
|
|||
### Creating the Data Transfer Object (DTO) |
|||
|
|||
[Application services](../../../Application-Services.md) typically get and return DTOs ([Data Transfer Objects](../../../Data-Transfer-Objects.md)) instead of entities. So, create a new `TodoItemDto` class under the `Services/Dtos` folder: |
|||
|
|||
```csharp |
|||
namespace TodoApp.Services.Dtos; |
|||
|
|||
public class TodoItemDto |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public string Text { get; set; } |
|||
} |
|||
``` |
|||
|
|||
This is a very simple DTO class that has the same properties as the `TodoItem` entity. Now, we are ready to implement our use-cases. |
|||
|
|||
### The Application Service Implementation |
|||
|
|||
Create a `TodoAppService` class under the `Services` folder of your project, as shown below: |
|||
|
|||
```csharp |
|||
using TodoApp.Entities; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace TodoApp.Services; |
|||
|
|||
public class TodoAppService : ApplicationService |
|||
{ |
|||
private readonly IRepository<TodoItem, Guid> _todoItemRepository; |
|||
|
|||
public TodoAppService(IRepository<TodoItem, Guid> todoItemRepository) |
|||
{ |
|||
_todoItemRepository = todoItemRepository; |
|||
} |
|||
|
|||
// TODO: Implement the methods here... |
|||
} |
|||
``` |
|||
|
|||
This class inherits from the `ApplicationService` class of the ABP Framework and implements our use-cases. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository<TodoItem, Guid>`, which is the default repository for the `TodoItem` entity. We will use it to implement our use cases. |
|||
|
|||
#### Getting the Todo Items |
|||
|
|||
Let's start by implementing the `GetListAsync` method, which is used to get a list of todo items: |
|||
|
|||
````csharp |
|||
public async Task<List<TodoItemDto>> GetListAsync() |
|||
{ |
|||
var items = await _todoItemRepository.GetListAsync(); |
|||
return items |
|||
.Select(item => new TodoItemDto |
|||
{ |
|||
Id = item.Id, |
|||
Text = item.Text |
|||
}).ToList(); |
|||
} |
|||
```` |
|||
|
|||
We are simply getting the `TodoItem` list from the repository, mapping them to the `TodoItemDto` objects and returning as the result. |
|||
|
|||
#### Creating a New Todo Item |
|||
|
|||
The next method is `CreateAsync` and we can implement it as shown below: |
|||
|
|||
````csharp |
|||
public async Task<TodoItemDto> CreateAsync(string text) |
|||
{ |
|||
var todoItem = await _todoItemRepository.InsertAsync( |
|||
new TodoItem {Text = text} |
|||
); |
|||
|
|||
return new TodoItemDto |
|||
{ |
|||
Id = todoItem.Id, |
|||
Text = todoItem.Text |
|||
}; |
|||
} |
|||
```` |
|||
|
|||
The repository's `InsertAsync` method inserts the given `TodoItem` to the database and returns the same `TodoItem` object. It also sets the `Id`, so we can use it on the returning object. We are simply returning a `TodoItemDto` by creating from the new `TodoItem` entity. |
|||
|
|||
#### Deleting a Todo Item |
|||
|
|||
Finally, we can implement the `DeleteAsync` as the following code block: |
|||
|
|||
````csharp |
|||
public async Task DeleteAsync(Guid id) |
|||
{ |
|||
await _todoItemRepository.DeleteAsync(id); |
|||
} |
|||
```` |
|||
|
|||
The application service is ready to be used from the UI layer. So, let's implement it. |
|||
|
|||
## User Interface |
|||
|
|||
It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI: |
|||
|
|||
 |
|||
|
|||
{{if UI=="MVC"}} |
|||
|
|||
### Index.cshtml.cs |
|||
|
|||
Open the `Index.cshtml.cs` file in the `Pages` folder and replace the content with the following code block: |
|||
|
|||
```csharp |
|||
using TodoApp.Services; |
|||
using TodoApp.Services.Dtos; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; |
|||
|
|||
namespace TodoApp.Pages; |
|||
|
|||
public class IndexModel : AbpPageModel |
|||
{ |
|||
public List<TodoItemDto> TodoItems { get; set; } |
|||
|
|||
private readonly TodoAppService _todoAppService; |
|||
|
|||
public IndexModel(TodoAppService todoAppService) |
|||
{ |
|||
_todoAppService = todoAppService; |
|||
} |
|||
|
|||
public async Task OnGetAsync() |
|||
{ |
|||
TodoItems = await _todoAppService.GetListAsync(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This class uses `TodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. |
|||
|
|||
### Index.cshtml |
|||
|
|||
Open the `Index.cshtml` file in the `Pages` folder and replace it with the following content: |
|||
|
|||
```xml |
|||
@page |
|||
@model TodoApp.Pages.IndexModel |
|||
|
|||
@section styles { |
|||
<abp-style src="/Pages/Index.cshtml.css" /> |
|||
} |
|||
@section scripts { |
|||
<abp-script src="/Pages/Index.cshtml.js" /> |
|||
} |
|||
|
|||
<div class="container"> |
|||
<abp-card> |
|||
<abp-card-header> |
|||
<abp-card-title> |
|||
TODO LIST |
|||
</abp-card-title> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<!-- FORM FOR NEW TODO ITEMS --> |
|||
<form id="NewItemForm" class="row row-cols-lg-auto g-3 align-items-center"> |
|||
<div class="col-12"> |
|||
<div class="input-group"> |
|||
<input id="NewItemText" type="text" class="form-control" placeholder="enter text..."> |
|||
</div> |
|||
</div> |
|||
<div class="col-12"> |
|||
<button type="submit" class="btn btn-primary">Submit</button> |
|||
</div> |
|||
</form> |
|||
<!-- TODO ITEMS LIST --> |
|||
<ul id="TodoList"> |
|||
@foreach (var todoItem in Model.TodoItems) |
|||
{ |
|||
<li data-id="@todoItem.Id"> |
|||
<i class="fa fa-trash-o"></i> @todoItem.Text |
|||
</li> |
|||
} |
|||
</ul> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
</div> |
|||
``` |
|||
|
|||
We are using ABP's [card tag helper](../../../UI/AspNetCore/Tag-Helpers/Cards.md) to create a simple card view. You could directly use the standard bootstrap HTML structure, however the ABP [tag helpers](../../../UI/AspNetCore/Tag-Helpers/Index.md) make it much easier and type safe. |
|||
|
|||
This page imports a CSS and a JavaScript file, so we should also create them. |
|||
|
|||
### Index.cshtml.js |
|||
|
|||
Open the `Index.cshtml.js` file in the `Pages` folder and replace with the following content: |
|||
|
|||
````js |
|||
$(function () { |
|||
|
|||
// DELETING ITEMS ///////////////////////////////////////// |
|||
$('#TodoList').on('click', 'li i', function(){ |
|||
var $li = $(this).parent(); |
|||
var id = $li.attr('data-id'); |
|||
|
|||
todoApp.services.todo.delete(id).then(function(){ |
|||
$li.remove(); |
|||
abp.notify.info('Deleted the todo item.'); |
|||
}); |
|||
}); |
|||
|
|||
// CREATING NEW ITEMS ///////////////////////////////////// |
|||
$('#NewItemForm').submit(function(e){ |
|||
e.preventDefault(); |
|||
|
|||
var todoText = $('#NewItemText').val(); |
|||
todoApp.services.todo.create(todoText).then(function(result){ |
|||
$('<li data-id="' + result.id + '">') |
|||
.html('<i class="fa fa-trash-o"></i> ' + result.text) |
|||
.appendTo($('#TodoList')); |
|||
$('#NewItemText').val(''); |
|||
}); |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
In the first part, we subscribed to the click events of the trash icons near the todo items, deleted the related item on the server and showed a notification on the UI. Also, we removed the deleted item from the DOM, so we wouldn't need to refresh the page. |
|||
|
|||
In the second part, we created a new todo item on the server. If it succeeded, we would then manipulate the DOM to insert a new `<li>` element to the todo list. This way, we wouldn't need to refresh the whole page after creating a new todo item. |
|||
|
|||
The interesting part here is how we communicate with the server. See the *Dynamic JavaScript Proxies & Auto API Controllers* section to understand how it works. But now, let's continue and complete the application. |
|||
|
|||
### Index.cshtml.css |
|||
|
|||
As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and replace with the following content: |
|||
|
|||
````css |
|||
#TodoList{ |
|||
list-style: none; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
#TodoList li { |
|||
padding: 5px; |
|||
margin: 5px 0px; |
|||
border: 1px solid #cccccc; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
#TodoList li i |
|||
{ |
|||
opacity: 0.5; |
|||
} |
|||
|
|||
#TodoList li i:hover |
|||
{ |
|||
opacity: 1; |
|||
color: #ff0000; |
|||
cursor: pointer; |
|||
} |
|||
```` |
|||
|
|||
This is a simple styling for the todo page. We believe that you can do much better :) |
|||
|
|||
Now, you can run the application again and see the result. |
|||
|
|||
### Dynamic JavaScript Proxies & Auto API Controllers |
|||
|
|||
In the `Index.cshtml.js` file, we've used the `todoApp.services.todo.delete(...)` and `todoApp.services.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. |
|||
|
|||
> `services` keyword comes from the namespace (`namespace TodoApp.Services;`). It's a naming convention. |
|||
|
|||
However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to **API Controllers** by convention. |
|||
|
|||
If you open [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: |
|||
|
|||
 |
|||
|
|||
{{else if UI=="BlazorServer"}} |
|||
|
|||
### Index.razor.cs |
|||
|
|||
Open the `Index.razor.cs` file in the `Pages` folder and replace the content with the following code block: |
|||
|
|||
```csharp |
|||
using Microsoft.AspNetCore.Components; |
|||
using TodoApp.Services; |
|||
using TodoApp.Services.Dtos; |
|||
|
|||
namespace TodoApp.Pages; |
|||
|
|||
public partial class Index |
|||
{ |
|||
[Inject] |
|||
private TodoAppService TodoAppService { get; set; } |
|||
|
|||
private List<TodoItemDto> TodoItems { get; set; } = new List<TodoItemDto>(); |
|||
private string NewTodoText { get; set; } |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
TodoItems = await TodoAppService.GetListAsync(); |
|||
} |
|||
|
|||
private async Task Create() |
|||
{ |
|||
var result = await TodoAppService.CreateAsync(NewTodoText); |
|||
TodoItems.Add(result); |
|||
NewTodoText = null; |
|||
} |
|||
|
|||
private async Task Delete(TodoItemDto todoItem) |
|||
{ |
|||
await TodoAppService.DeleteAsync(todoItem.Id); |
|||
await Notify.Info("Deleted the todo item."); |
|||
TodoItems.Remove(todoItem); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This class uses the `TodoAppService` to get the list of todo items. It manipulates the `TodoItems` list after create and delete operations. This way, we don't need to refresh the whole todo list from the server. |
|||
|
|||
### Index.razor |
|||
|
|||
Open the `Index.razor` file in the `Pages` folder and replace the content with the following code block: |
|||
|
|||
```xml |
|||
@page "/" |
|||
@inherits TodoAppComponentBase |
|||
|
|||
<div class="container"> |
|||
<Card> |
|||
<CardHeader> |
|||
<CardTitle> |
|||
TODO LIST |
|||
</CardTitle> |
|||
</CardHeader> |
|||
<CardBody> |
|||
<!-- FORM FOR NEW TODO ITEMS --> |
|||
<form id="NewItemForm" @onsubmit:preventDefault @onsubmit="() => Create()" class="row row-cols-lg-auto g-3 align-items-center"> |
|||
<div class="col-12"> |
|||
<div class="input-group"> |
|||
<input name="NewTodoText" type="text" @bind-value="@NewTodoText" class="form-control" placeholder="enter text..." /> |
|||
</div> |
|||
</div> |
|||
<div class="col-12"> |
|||
<button type="submit" class="btn btn-primary">Submit</button> |
|||
</div> |
|||
</form> |
|||
<!-- TODO ITEMS LIST --> |
|||
<ul id="TodoList"> |
|||
@foreach (var todoItem in TodoItems) |
|||
{ |
|||
<li data-id="@todoItem.Id"> |
|||
<i class="far fa-trash-alt" |
|||
@onclick="() => Delete(todoItem)"></i> |
|||
@todoItem.Text |
|||
</li> |
|||
} |
|||
</ul> |
|||
</CardBody> |
|||
</Card> |
|||
</div> |
|||
``` |
|||
|
|||
### Index.razor.css |
|||
|
|||
As the final touch, open the `Index.razor.css` file in the `Pages` folder and replace it with the following content: |
|||
|
|||
````css |
|||
#TodoList{ |
|||
list-style: none; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
#TodoList li { |
|||
padding: 5px; |
|||
margin: 5px 0px; |
|||
border: 1px solid #cccccc; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
#TodoList li i |
|||
{ |
|||
opacity: 0.5; |
|||
} |
|||
|
|||
#TodoList li i:hover |
|||
{ |
|||
opacity: 1; |
|||
color: #ff0000; |
|||
cursor: pointer; |
|||
} |
|||
```` |
|||
|
|||
This is a simple styling for the todo page. We believe that you can do much better :) |
|||
|
|||
Now, you can run the application again to see the result. |
|||
|
|||
{{else if UI=="NG"}} |
|||
|
|||
### Service Proxy Generation |
|||
|
|||
ABP provides a handy feature to automatically create client-side services to easily consume HTTP APIs provided by the server. |
|||
|
|||
You first need to run the `TodoApp` project since the proxy generator reads API definitions from the server application. |
|||
|
|||
Once you run the `TodoApp` project (**Swagger API Definition** will be shown), open a command-line terminal in the directory of `angular` folder and run the following command: |
|||
|
|||
```bash |
|||
abp generate-proxy -t ng |
|||
``` |
|||
|
|||
If everything goes well, it should generate an output as shown below: |
|||
|
|||
```bash |
|||
CREATE src/app/proxy/generate-proxy.json (182755 bytes) |
|||
CREATE src/app/proxy/README.md (1000 bytes) |
|||
CREATE src/app/proxy/services/todo.service.ts (833 bytes) |
|||
CREATE src/app/proxy/services/dtos/models.ts (71 bytes) |
|||
CREATE src/app/proxy/services/dtos/index.ts (26 bytes) |
|||
CREATE src/app/proxy/services/index.ts (81 bytes) |
|||
CREATE src/app/proxy/index.ts (61 bytes) |
|||
``` |
|||
|
|||
Then, we can use the `TodoService` to use the server-side HTTP APIs, as we'll do in the next section. |
|||
|
|||
### home.component.ts |
|||
|
|||
Open the `/angular/src/app/home/home.component.ts` file and replace its content with the following code block: |
|||
|
|||
```ts |
|||
import { ToasterService } from "@abp/ng.theme.shared"; |
|||
import { Component, OnInit } from '@angular/core'; |
|||
import { TodoItemDto } from "@proxy/services/dtos"; |
|||
import { TodoService } from "@proxy/services"; |
|||
|
|||
@Component({ |
|||
selector: 'app-home', |
|||
templateUrl: './home.component.html', |
|||
styleUrls: ['./home.component.scss'], |
|||
}) |
|||
|
|||
export class HomeComponent implements OnInit { |
|||
|
|||
todoItems: TodoItemDto[]; |
|||
newTodoText: string; |
|||
|
|||
constructor( |
|||
private todoService: TodoService, |
|||
private toasterService: ToasterService) |
|||
{ } |
|||
|
|||
ngOnInit(): void { |
|||
this.todoService.getList().subscribe(response => { |
|||
this.todoItems = response; |
|||
}); |
|||
} |
|||
|
|||
create(): void{ |
|||
this.todoService.create(this.newTodoText).subscribe((result) => { |
|||
this.todoItems = this.todoItems.concat(result); |
|||
this.newTodoText = null; |
|||
}); |
|||
} |
|||
|
|||
delete(id: string): void { |
|||
this.todoService.delete(id).subscribe(() => { |
|||
this.todoItems = this.todoItems.filter(item => item.id !== id); |
|||
this.toasterService.info('Deleted the todo item.'); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
We've used `TodoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used on the view side. |
|||
|
|||
### home.component.html |
|||
|
|||
Open the `/angular/src/app/home/home.component.html` file and replace its content with the following code block: |
|||
|
|||
````html |
|||
<div class="container"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<div class="card-title">TODO LIST</div> |
|||
</div> |
|||
<div class="card-body"> |
|||
<!-- FORM FOR NEW TODO ITEMS --> |
|||
<form class="row row-cols-lg-auto g-3 align-items-center" (ngSubmit)="create()"> |
|||
<div class="col-12"> |
|||
<div class="input-group"> |
|||
<input name="NewTodoText" type="text" [(ngModel)]="newTodoText" class="form-control" placeholder="enter text..." /> |
|||
</div> |
|||
</div> |
|||
<div class="col-12"> |
|||
<button type="submit" class="btn btn-primary">Submit</button> |
|||
</div> |
|||
</form> |
|||
<!-- TODO ITEMS LIST --> |
|||
<ul id="TodoList"> |
|||
<li *ngFor="let todoItem of todoItems"> |
|||
<i class="fa fa-trash-o" (click)="delete(todoItem.id)"></i> {%{{{ todoItem.text }}}%} |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
```` |
|||
|
|||
### home.component.scss |
|||
|
|||
As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: |
|||
|
|||
````css |
|||
#TodoList{ |
|||
list-style: none; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
#TodoList li { |
|||
padding: 5px; |
|||
margin: 5px 0px; |
|||
border: 1px solid #cccccc; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
#TodoList li i |
|||
{ |
|||
opacity: 0.5; |
|||
} |
|||
|
|||
#TodoList li i:hover |
|||
{ |
|||
opacity: 1; |
|||
color: #ff0000; |
|||
cursor: pointer; |
|||
} |
|||
```` |
|||
|
|||
This is a simple styling for the todo page. We believe that you can do much better :) |
|||
|
|||
Now, you can run the application again to see the result. |
|||
|
|||
{{end}} |
|||
|
|||
## Conclusion |
|||
|
|||
In this tutorial, we've built a very simple application to warm up with the ABP Framework. |
|||
|
|||
## Source Code |
|||
|
|||
You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). |
|||
|
|||
## See Also |
|||
|
|||
* Check the [Web Application Development Tutorial](../../Part-1.md) to see a real-life web application development in a layered architecture using the [Application Startup Template](../../../Startup-Templates/Application.md). |
|||
|
After Width: | Height: | Size: 8.7 KiB |
@ -0,0 +1,29 @@ |
|||
# DateTime Format Pipes |
|||
|
|||
You can format date by Date pipe of angular. |
|||
|
|||
Example |
|||
|
|||
```html |
|||
<span> {{today | date 'dd/mm/yy'}}</span> |
|||
``` |
|||
|
|||
ShortDate, ShortTime and ShortDateTime format data like angular's data pipe but easier. Also the pipes get format from config service by culture. |
|||
|
|||
# ShortDate Pipe |
|||
|
|||
```html |
|||
<span> {{today | shortDatePipe }}</span> |
|||
``` |
|||
|
|||
# ShortTime Pipe |
|||
|
|||
```html |
|||
<span> {{today | shortTimePipe }}</span> |
|||
``` |
|||
|
|||
# ShortDateTime Pipe |
|||
|
|||
```html |
|||
<span> {{today | shortDateTimePipe }}</span> |
|||
``` |
|||
@ -0,0 +1,47 @@ |
|||
# Angular: Global Features API |
|||
|
|||
The `ConfigStateService.getGlobalFeatures` API allows you to get the enabled features of the [Global Features](../../Global-Features.md) on 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 |
|||
|
|||
import { ConfigStateService } from '@abp/ng.core'; |
|||
import { Component, OnInit } from '@angular/core'; |
|||
|
|||
@Component({ |
|||
/* class metadata here */ |
|||
}) |
|||
class DemoComponent implements OnInit { |
|||
constructor(private config: ConfigStateService) {} |
|||
|
|||
ngOnInit(): void { |
|||
// Gets all enabled global features. |
|||
const getGlobalFeatures = this.config.getGlobalFeatures(); |
|||
|
|||
//Example result is: `{ enabledFeatures: [ 'Shopping.Payment', 'Ecommerce.Subscription' ] }` |
|||
|
|||
// or |
|||
this.config.getGlobalFeatures$().subscribe(getGlobalFeatures => { |
|||
// use getGlobalFeatures here |
|||
}) |
|||
|
|||
// Check the global feature is enabled |
|||
this.config.getGlobalFeatureIsEnabled('Ecommerce.Subscription') |
|||
|
|||
//Example result is `true` |
|||
|
|||
this.config.getGlobalFeatureIsEnabled('My.Subscription') |
|||
|
|||
//Example result is `false` |
|||
|
|||
// or |
|||
this.config.getGlobalFeatureIsEnabled$('Ecommerce.Subscription').subscribe((isEnabled:boolean) => { |
|||
// use isEnabled here |
|||
}) |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,44 @@ |
|||
# Loading Directive |
|||
|
|||
|
|||
You may want to block a part of the UI and show a spinner for a while; the `LoadingDirective` directive makes this for you. `LoadingDirective` has been exposed by the `@abp/ng.theme.shared` package. |
|||
|
|||
|
|||
## Getting Started |
|||
|
|||
In order to use the `LoadingDirective` in an HTML template, the **`ThemeSharedModule`** should be imported into your module like this: |
|||
|
|||
```js |
|||
// ... |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
|
|||
@NgModule({ |
|||
//... |
|||
imports: [..., ThemeSharedModule], |
|||
}) |
|||
export class MyFeatureModule {} |
|||
``` |
|||
|
|||
|
|||
## Usage |
|||
|
|||
The `LoadingDirective` is easy to use. The directive's selector is **`abpLoading`**. By adding the `abpLoading` attribute to an HTML element, you can activate the `LoadingDirectiveective` for the HTML element when the value is true. |
|||
|
|||
See an example usage: |
|||
|
|||
```html |
|||
<div [abpLoading]="true"> |
|||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam commodi quae aspernatur, |
|||
corporis velit et suscipit id consequuntur amet minima expedita cum reiciendis dolorum |
|||
cupiditate? Voluptas eaque voluptatum odio deleniti quo vel illum nemo accusamus nulla ratione |
|||
impedit dolorum expedita necessitatibus fugiat ullam beatae, optio eum cupiditate ducimus |
|||
architecto. |
|||
</div> |
|||
``` |
|||
|
|||
|
|||
The `abpLoading` attribute has been added to the `<div>` element that contains very a long text inside to activate the `LoadingDirective`. |
|||
|
|||
See the result: |
|||
|
|||
 |
|||
|
After Width: | Height: | Size: 54 KiB |
@ -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,27 @@ |
|||
# 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. The server side uses OpenIddict to handle the authentication. |
|||
* 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}} |
|||