@ -0,0 +1,94 @@ |
|||
# Contributor Covenant Code of Conduct |
|||
|
|||
## Preamble |
|||
|
|||
The ABP Framework was created to implement common generic web application features in a common code base. This approach helps developers to decouple line of business application features from their custom business logic. Besides, the ABP Framework helps create MVPs and kick-start your project as quickly as possible. All the contributors try to save valuable time by automating repetitive tasks at the framework level. This code of conduct has been adopted by many [other open source communities](http://contributor-covenant.org/adopters/) and we feel it expresses our values well. |
|||
|
|||
|
|||
|
|||
## Our Pledge |
|||
|
|||
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. |
|||
|
|||
|
|||
|
|||
## Our Standards |
|||
|
|||
Examples of behavior that contributes to a positive environment for our community include: |
|||
|
|||
- Demonstrating empathy and kindness toward other people |
|||
- Being respectful of differing opinions, viewpoints, and experiences |
|||
- Giving and gracefully accepting constructive feedback |
|||
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience |
|||
- Focusing on what is best not just for us as individuals but for the overall community |
|||
|
|||
Examples of unacceptable behavior include: |
|||
|
|||
- The use of sexualized language or imagery, and sexual attention or advances of any kind |
|||
- Trolling, insulting or derogatory comments, and personal or political attacks |
|||
- Public or private harassment |
|||
- Publishing others’ private information, such as a physical or email address, without their explicit permission |
|||
- Other conduct which could reasonably be considered inappropriate in a professional setting |
|||
|
|||
|
|||
|
|||
## Enforcement Responsibilities |
|||
|
|||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. |
|||
|
|||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct and will communicate reasons for moderation decisions when appropriate. |
|||
|
|||
|
|||
|
|||
## Scope |
|||
|
|||
This Code of Conduct applies within all community spaces and when an individual officially represents the community in public spaces. Examples of representing our community include: |
|||
|
|||
- Using an official e-mail address. |
|||
- Posting via an official social media account. |
|||
- Acting as an appointed representative at an online or offline event. |
|||
|
|||
|
|||
|
|||
## Enforcement |
|||
|
|||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [info@abp.io](mailto:info@abp.io). All complaints will be reviewed and investigated promptly and fairly. All community leaders must to respect the privacy and security of the reporter of any incident. |
|||
|
|||
|
|||
|
|||
## Enforcement Guidelines |
|||
|
|||
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: |
|||
|
|||
### 1. Correction |
|||
|
|||
**Community Impact:** Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. |
|||
|
|||
**Consequence:** A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. |
|||
|
|||
### 2. Warning |
|||
|
|||
**Community Impact:** A violation through a single incident or series of actions. |
|||
|
|||
**Consequence:** A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period. This includes avoiding interactions in community spaces and external channels like social media. Violating these terms may lead to a temporary or permanent ban. |
|||
|
|||
### 3. Temporary Ban |
|||
|
|||
**Community Impact:** A serious violation of community standards, including sustained inappropriate behavior. |
|||
|
|||
**Consequence:** A temporary ban from any interaction or public communication with the community for a specified period. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. |
|||
|
|||
### 4. Permanent Ban |
|||
|
|||
**Community Impact:** Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. |
|||
|
|||
**Consequence:** A permanent ban from any sort of public interaction within the community. |
|||
|
|||
|
|||
|
|||
## Attribution |
|||
|
|||
This document is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. |
|||
Community Impact Guidelines were inspired by [Mozilla’s code of conduct enforcement ladder](https://github.com/mozilla/diversity). |
|||
For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. |
|||
Translations are available at https://www.contributor-covenant.org/translations. |
|||
@ -1,9 +1,9 @@ |
|||
<Project> |
|||
<ItemGroup> |
|||
<ItemGroup Condition="'$(Configuration)' == 'Release'"> |
|||
<PackageReference Include="ConfigureAwait.Fody" Version="3.3.1" PrivateAssets="All" /> |
|||
<PackageReference Include="Fody" Version="6.6.1"> |
|||
<PrivateAssets>All</PrivateAssets> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
</Project> |
|||
</Project> |
|||
|
|||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 613 KiB After Width: | Height: | Size: 613 KiB |
|
Before Width: | Height: | Size: 523 KiB After Width: | Height: | Size: 523 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,211 @@ |
|||
# ABP.IO Platform 7.2 RC Has Been Released |
|||
|
|||
Today, we are happy to release the [ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) version **7.2 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. |
|||
|
|||
Try this version and provide feedback for a more stable version of ABP v7.2! Thanks to all of you. |
|||
|
|||
## Get Started with the 7.2 RC |
|||
|
|||
Follow the steps below to try version 7.2.0 RC today: |
|||
|
|||
1) **Upgrade** the ABP CLI to version `7.2.0-rc.1` using a command line terminal: |
|||
|
|||
````bash |
|||
dotnet tool update Volo.Abp.Cli -g --version 7.2.0-rc.1 |
|||
```` |
|||
|
|||
**or install** it if you haven't before: |
|||
|
|||
````bash |
|||
dotnet tool install Volo.Abp.Cli -g --version 7.2.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 [Get Started](https://abp.io/get-started) page to generate a CLI command to create a new application. |
|||
|
|||
You can use any IDE that supports .NET 7.x, like [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/). |
|||
|
|||
## Migration Guides |
|||
|
|||
There are breaking changes in this version that may affect your application. |
|||
Please see the following migration documents, if you are upgrading from v7.1: |
|||
|
|||
* [ABP Framework 7.1 to 7.2 Migration Guide](https://docs.abp.io/en/abp/7.2/Migration-Guides/Abp-7_2) |
|||
* [ABP Commercial 7.1 to 7.2 Migration Guide](https://docs.abp.io/en/commercial/7.2/migration-guides/v7_2) |
|||
|
|||
|
|||
## What's New with ABP Framework 7.2? |
|||
|
|||
In this section, I will introduce some major features released in this version. Here is a brief list of the titles that will be explained in the next sections: |
|||
|
|||
* Grouping of Navigation Menu Items |
|||
* Introducing the `BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService` |
|||
* CMS Kit Comments: Don't Allow External URLs |
|||
* Angular UI New Components |
|||
* Others |
|||
|
|||
### Grouping of Navigation Menu Items |
|||
|
|||
Some applications may need to group their main menus to tidy up their menu structure. For example, you may want to group ABP's menu items, which came from modules in a group named *Admin*. |
|||
|
|||
In this version, you can allow to define groups and associate menu items with a group. Then your theme can render your menu items within the specified groups. |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
//Creating a new group |
|||
context.Menu.AddGroup("Dashboards", l["Dashboards"]); |
|||
|
|||
//Setting the group name for menu items |
|||
context.Menu |
|||
.AddItem(new ApplicationMenuItem("Home", l["Menu:Home"], groupName: "Dashboards") |
|||
.AddItem(new ApplicationMenuItem("Home", l["Menu:Dashboard"], groupName: "Dashboards"); |
|||
} |
|||
``` |
|||
|
|||
> **Note**: Currently, only the [LeptonX Theme](https://leptontheme.com/) renders groups for menu items. See the "LeptonX - Render Groups for Menu Items" section below for a demonstration. |
|||
|
|||
### Introducing the `BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService` |
|||
|
|||
In this version, we have introduced the `BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService` service to re-initialize application configurations. This service can be helpful, if you want to reset the application configurations after changing some configurations through your code. For example, you might have changed the values of some settings and might want to be able to get the new settings without the need to refresh the page. For this purpose, the `BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.ResetAsync()` method can be used to re-initialize the application configurations and cache the updated configurations for further usages. |
|||
|
|||
> For more information, please see [https://github.com/abpframework/abp/issues/15887](https://github.com/abpframework/abp/issues/15887). |
|||
|
|||
### CMS Kit Comments: Disallowing External URLs |
|||
|
|||
CMS Kit provides a [comment system](https://docs.abp.io/en/abp/7.2/Modules/Cms-Kit/Comments) to add the comment feature to any kind of resource, like blog posts for an example. The CMS Kit comment section is good for visitor comments and can improve your interaction with your application users. |
|||
|
|||
Sometimes, malicious users (or bots) can submit advertisement links into the comment sections. With this version, you can specify *allowed external URLs* for a specific comment section and disallow any other external URLs. You just need to configure the `CmsKitCommentOptions` as follows: |
|||
|
|||
```csharp |
|||
Configure<CmsKitCommentOptions>(options => |
|||
{ |
|||
options.AllowedExternalUrls = new Dictionary<string, List<string>> |
|||
{ |
|||
{ |
|||
"Product", |
|||
new List<string> |
|||
{ |
|||
"https://abp.io/" |
|||
} |
|||
} |
|||
}; |
|||
}); |
|||
``` |
|||
|
|||
If you don't specify any allowed external URLs for a specific comment section, all external URLs are allowed to be used in comments. For more information, please refer to the [CMS Kit: Comments documentation](https://docs.abp.io/en/abp/latest/Modules/Cms-Kit/Comments). |
|||
|
|||
### New Components for Angular UI |
|||
|
|||
In this version, we have created some useful UI components for Angular UI, which are `abp-checkbox`, `abp-form-input`, and `abp-card`. Instead of using the related HTML elements and specifying bootstrap classes, from this version on, you can use these components. |
|||
|
|||
You can see the following examples for the usage of the `abp-card` component: |
|||
|
|||
```html |
|||
<abp-card cardClass="mt-4 mb-5"> |
|||
<abp-card-body> |
|||
<div>...</div> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
> See the [Card Component documentation](https://docs.abp.io/en/abp/7.2/UI/Angular/Card-Component) for more information. |
|||
|
|||
### Others |
|||
|
|||
* OpenIddict registered custom scopes have been added to the openid-configuration endpoint (`/.well-known/openid-configuration`) automatically. See [#16141](https://github.com/abpframework/abp/issues/16141) for more information. |
|||
* Two new tag-helpers have been added to MVC UI, which are `abp-date-picker` and `abp-date-range-picker`. See [#15806](https://github.com/abpframework/abp/pull/15806) for more information. |
|||
* Filtering/searching has been improved in the Docs Module and unified under a single *Search* section. See [#15787](https://github.com/abpframework/abp/issues/15787) for more information. |
|||
|
|||
## What's New with ABP Commercial 7.2? |
|||
|
|||
We've also worked on [ABP Commercial](https://commercial.abp.io/) to align the features and changes made in the ABP Framework. The following sections introduce a few new features coming with ABP Commercial 7.2. |
|||
|
|||
### Authority Delegation |
|||
|
|||
Authority Delegation is a way of delegating the responsibility of the current user to a different user(s) for a limited time. Thus, a user can be switched to the delegated users' account and perform actions on their behalf. |
|||
|
|||
This version introduces support for the **Authority Delegation** in the [Account Module](https://docs.abp.io/en/commercial/latest/modules/account). You can check the following gif for a demonstration: |
|||
|
|||
 |
|||
|
|||
### Force Password Change at Next Logon |
|||
|
|||
It's a typical need to force users to change their password after their first successful login. Especially, if you as admin create a new user (*from the Users page of the Identity Pro module*, for example) with an easy initial password or a randomly generated password. The user should change his/her password with a more secure password that only they know. |
|||
|
|||
In this version, the "Forcing Password Change at Next Logon" feature has been added for this kind of purpose. Now, it's possible to force a user to change their password on the next login. |
|||
|
|||
The admin only needs to check the *Should change password on next login* option, while creating a new user: |
|||
|
|||
 |
|||
|
|||
After the first successful login, a password change page will open and force the user to change their password: |
|||
|
|||
 |
|||
|
|||
Then, the user starts using their account with a secure password that only they know. |
|||
|
|||
### Periodic Password Changes (Password Aging) |
|||
|
|||
**Password aging** is a mechanism to force users to periodically change their passwords. It allows you to specify a max number of days that a password can be used before it has to be changed. |
|||
|
|||
 |
|||
|
|||
You can force this behavior in the "Password renewing settings" section of the Settings page as can be seen in the image above. Then, after the specified time has passed, users will have to renew their passwords. |
|||
|
|||
### LeptonX - Render Groups for Menu Items |
|||
|
|||
As mentioned in the *Grouping of Navigation Menu Items* section above, the [LeptonX Theme](https://leptontheme.com/) renders groups for menu items: |
|||
|
|||
 |
|||
|
|||
### Suite: Show Properties on Create/Update/List Pages |
|||
|
|||
In this version, ABP Suite allows you to choose whether a property is visible/invisible on the create/update modals and list page. It also allows you to set specific properties to *readonly* on the update modals. |
|||
|
|||
 |
|||
|
|||
## Community News |
|||
|
|||
### ABP - DOTNET CONF'23 |
|||
|
|||
 |
|||
|
|||
As the ABP team, we've organized 10+ [online events](https://community.abp.io/events) and gained a good experience with software talks. We are organizing ABP Dotnet Conference 2023, a full-featured software conference, in May. You can visit [https://abp.io/conference](https://abp.io/conference) to see speakers, talks, schedules, and other details. |
|||
|
|||
**Less than a month left until the event**! Don't forget to take your seat and buy an early bird ticket from [https://kommunity.com/volosoft/events/1st-abp-conference-96db1a54](https://kommunity.com/volosoft/events/1st-abp-conference-96db1a54)! |
|||
|
|||
### New ABP Community Posts |
|||
|
|||
There are exciting articles contributed by the ABP community as always. I will highlight some of them here: |
|||
|
|||
* [What’s New in .NET 8 🧐 ? Discover ALL .NET 8 Features](https://community.abp.io/posts/whats-new-in-.net-8-discover-all-.net-8-features-llcmrdre) by [Alper Ebicoglu](https://twitter.com/alperebicoglu). |
|||
* [Converting Create/Edit Modal to Page - Blazor](https://community.abp.io/posts/converting-createedit-modal-to-page-blazor-eexdex8y) by [Enis Necipoglu](https://twitter.com/EnisNecipoglu). |
|||
* [Using Dapper with the ABP Framework](https://community.abp.io/posts/using-dapper-with-the-abp-framework-shp74p2l) by [Halil Ibrahim Kalkan](https://twitter.com/hibrahimkalkan). |
|||
* [ABP React Template](https://community.abp.io/posts/abp-react-template-33pjmran) by [Anto Subash](https://twitter.com/antosubash). |
|||
* [How to Export Data to Excel Files with ASP.NET Core Minimal API](https://community.abp.io/posts/how-to-export-data-to-excel-files-with-asp.net-core-minimal-api-79o45u3s) by [Berkan Sasmaz](https://twitter.com/berkansasmazz). |
|||
|
|||
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/articles/submit) to the ABP Community. |
|||
|
|||
### New ABP Blog Posts |
|||
|
|||
There are also some exciting blog posts written by the ABP team. You can see the following list for some of those articles: |
|||
|
|||
* [ABP Framework: Open Source Web Application Development Framework](https://blog.abp.io/abp/open-source-web-application-development-framework) by [Alper Ebicoglu](https://twitter.com/alperebicoglu). |
|||
* [ABP Framework: The Ultimate .NET Web Framework for Rapid Application Development](https://blog.abp.io/abp/ultimate-net-web-framework-for-rapid-application-development) by [Alper Ebicoglu](https://twitter.com/alperebicoglu). |
|||
* [Top 10 .NET Core Libraries Every Developer Should Know 🔥](https://blog.abp.io/abp/Top-10-.NET-Core-Libraries-Every-Developer-Should-Know) by [Alper Ebicoglu](https://twitter.com/alperebicoglu) |
|||
|
|||
## Conclusion |
|||
|
|||
This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://docs.abp.io/en/abp/7.2/Road-Map) documentation to learn about the release schedule and planned features for the next releases. Please try ABP v7.2 RC and provide feedback to help us release a more stable version. |
|||
|
|||
Thanks for being a part of this community! |
|||
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 48 KiB |
@ -0,0 +1,118 @@ |
|||
# 💻 How to Optimize Your ASP.NET Application for Improved Performance 🚀 |
|||
|
|||
If you want your ASP.NET application to perform well, you need to optimize it for speed, responsiveness, and user experience. Performance optimization is critical for factors like fast page load times, improved response efficiency, and happy users. In this article, I'll provide several tips and tricks to help you optimize performance in ASP.NET Core. |
|||
|
|||
## 🚀 Use Response Compression in Your ASP.NET Application |
|||
You can use ASP.NET Core's built-in response compression middleware to compress the response data and reduce the amount of data that needs to be transferred over the network. To use response compression, add the following code to your application's Startup.cs file: |
|||
|
|||
```csharp |
|||
services.AddResponseCompression(options => |
|||
{ |
|||
options.EnableForHttps = true; |
|||
}); |
|||
|
|||
app.UseResponseCompression(); |
|||
``` |
|||
|
|||
## 🖼️ Optimize Images in Your ASP.NET Application: |
|||
|
|||
Images can be a major contributor to page bloat and slow load times. Here are some tips to optimize images: |
|||
|
|||
🖌️ Use a tool like ImageOptim or Kraken.io to compress and optimize images. |
|||
|
|||
🖼️ Specify the width and height of images in HTML so the browser can allocate space for them before they load. |
|||
|
|||
📝 Use alt attributes to provide descriptive text for images, which can improve accessibility and also help with SEO. |
|||
|
|||
📜 Use lazy loading for images that are below the fold, meaning they're not visible on the initial screen view. You can use libraries like Vanilla LazyLoad to implement lazy loading. |
|||
|
|||
📱 Use responsive images to serve different image sizes to different devices. This can improve page load times by reducing the size of images that are displayed on smaller devices. |
|||
|
|||
💻 Example: |
|||
|
|||
```html |
|||
<picture> |
|||
<source media="(min-width: 650px)" data-srcset="image.webp"> |
|||
<source media="(min-width: 465px)" data-srcset="image_small.webp"> |
|||
<img src="placeholder.png" data-src="image.webp" alt="Image" width="100" height="100" class="lazy" /> |
|||
</picture> |
|||
``` |
|||
|
|||
```javascript |
|||
var lazyLoadInstance = new LazyLoad(); |
|||
``` |
|||
|
|||
## 🧱 Optimize HTML in Your ASP.NET Application: |
|||
|
|||
The structure and organization of HTML can affect the page speed. Here are some tips to optimize HTML: |
|||
|
|||
📝 Use the heading tags (h1, h2, h3, etc.) in a logical and sequential order. |
|||
|
|||
🔩 Use the "defer" attribute for script tags that don't need to be executed immediately. This can improve the page load times by delaying the execution of scripts until after the page has rendered. |
|||
|
|||
🔩 Use the "async" attribute for script tags that can be executed asynchronously. This can further improve the page load times by allowing scripts to be downloaded and executed simultaneously. |
|||
|
|||
🧱 Use semantic HTML elements (like nav, section, and article) to provide additional structure and meaning to the page. |
|||
|
|||
## 🎨 Optimize CSS and JavaScript in Your ASP.NET Application: |
|||
|
|||
CSS and JavaScript files can be a major contributor to the page load times. Here are some tips to optimize CSS and JavaScript in your ASP.NET application: |
|||
|
|||
🔨 Minify and concatenate CSS and JavaScript files to reduce their size. |
|||
|
|||
🔩 Use the "defer" or "async" attributes for script tags to delay or asynchronously load scripts. |
|||
|
|||
## 🔡 Use system fonts in Your ASP.NET Application: |
|||
|
|||
Loading custom fonts can be slow and increase page load times. Using system fonts can improve page speed by allowing the browser to use fonts that are already installed on the user's device. |
|||
|
|||
## 🖼️ Use Placeholders and Progress Indicators in Your ASP.NET Application: |
|||
|
|||
To improve the perceived performance of your website, you can use placeholders and progress indicators for slow-loading sections of your page. You can use JavaScript to load these sections after the initial page load. |
|||
|
|||
💻 Example: |
|||
|
|||
```html |
|||
|
|||
<div id="placeholder" data-url="/slow-loading-content"> |
|||
<p>Loading...</p> |
|||
</div> |
|||
``` |
|||
|
|||
```javascript |
|||
const placeholder = document.querySelector('#placeholder'); |
|||
fetch(placeholder.dataset.url) |
|||
.then(response => response.text()) |
|||
.then(html => placeholder.innerHTML = html); |
|||
``` |
|||
|
|||
## 🔗 Use the Appropriate Link Text and ARIA Labels: |
|||
|
|||
When using links, use appropriate link texts that accurately describe the content of the linked page. This can improve the accessibility and also help with SEO. |
|||
|
|||
ARIA labels should also be used to provide additional context for links. This can also improve the accessibility and help with SEO. |
|||
|
|||
💻 Example: |
|||
|
|||
```html |
|||
<a href="https://example.com/" aria-label="Go to Example">Example</a> |
|||
<a href="https://example.com/" aria-label="Go to Another Example">Another Example</a> |
|||
``` |
|||
|
|||
## 🌐 Optimize the Third-party Resources in Your ASP.NET Application: |
|||
|
|||
Third-party resources like social media widgets and advertising scripts can slow down the page load times. Here are some tips to optimize third-party resources: |
|||
|
|||
🔩 Use asynchronous scripts when possible. |
|||
|
|||
🔍 Only load third-party resources that are necessary for the page. |
|||
|
|||
By following these optimization techniques, you can significantly improve the page speed of your ASP.NET Core web application. |
|||
|
|||
## What is ABP Framework? |
|||
|
|||
ABP Framework offers an opinionated architecture to build enterprise software solutions with ASP.NET Core best practices on top of the .NET and the ASP.NET Core platforms. It provides the fundamental web application infrastructure, production-ready dotnet startup templates, modules, asp.net core ui themes, tooling, guides and documentation to implement that ASP.NET core architecture properly and automate the details and repetitive work as much as possible. |
|||
|
|||
If you are starting a new ASP.NET Core project, try [abp.io](https://abp.io/) now... |
|||
|
|||
**IT IS FREE AND OPEN-SOURCE!** |
|||
@ -0,0 +1,86 @@ |
|||
# ABP.IO Platform 7.2 Final Has Been Released! |
|||
|
|||
[ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 7.2 versions have been released today. |
|||
|
|||
## What's New With Version 7.2? |
|||
|
|||
All the new features were already explained in detail in the [7.2 RC Announcement Post](https://blog.abp.io/abp/ABP.IO-Platform-7.2-RC-Has-Been-Published), so no need to go over them again. Check it out for more details. |
|||
|
|||
## Getting Started with 7.2 |
|||
|
|||
### Creating New Solutions |
|||
|
|||
You can create a new solution with the ABP Framework version 7.2 by either using the `abp new` command or generating the CLI command on the [get started page](https://abp.io/get-started). |
|||
|
|||
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for more. |
|||
|
|||
### How to Upgrade an Existing Solution |
|||
|
|||
#### Install/Update the ABP CLI |
|||
|
|||
First of all, install the ABP CLI or upgrade it to the latest version. |
|||
|
|||
If you haven't installed it yet: |
|||
|
|||
```bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
``` |
|||
|
|||
To update the existing CLI: |
|||
|
|||
```bash |
|||
dotnet tool update -g Volo.Abp.Cli |
|||
``` |
|||
|
|||
#### Upgrading Existing Solutions with the ABP Update Command |
|||
|
|||
[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command: |
|||
|
|||
```bash |
|||
abp update |
|||
``` |
|||
|
|||
Run this command in the root folder of your solution. |
|||
|
|||
## Migration Guides |
|||
|
|||
There are breaking changes in this version that may affect your application. |
|||
Please see the following migration documents, if you are upgrading from v7.1: |
|||
|
|||
* [ABP Framework 7.1 to 7.2 Migration Guide](https://docs.abp.io/en/abp/7.2/Migration-Guides/Abp-7_2) |
|||
* [ABP Commercial 7.1 to 7.2 Migration Guide](https://docs.abp.io/en/commercial/7.2/migration-guides/v7_2) |
|||
|
|||
## Community News |
|||
|
|||
### ABP - DOTNET CONF'23 |
|||
|
|||
 |
|||
|
|||
As the ABP team, we've organized 10+ [online events](https://community.abp.io/events) and gained a good experience with software talks. We are organizing ABP Dotnet Conference 2023, a full-featured software conference, on May 10. You can visit [https://abp.io/conference](https://abp.io/conference) to see the speakers, talks, schedules, and other details. |
|||
|
|||
Don't forget to take your seat and buy a ticket from [https://kommunity.com/volosoft/events/1st-abp-conference-96db1a54](https://kommunity.com/volosoft/events/1st-abp-conference-96db1a54)! |
|||
|
|||
### New ABP Community Posts |
|||
|
|||
There are exciting articles contributed by the ABP community as always. I will highlight some of them here: |
|||
|
|||
* [Converting Create/Edit Modal to Page AngularUI](https://community.abp.io/posts/converting-createedit-modal-to-page-angularui-doadhgil) by [Masum Ulu](https://twitter.com/masumulu) |
|||
* [How to Export Data to Excel Files with ASP.NET Core Minimal API](https://community.abp.io/posts/how-to-export-data-to-excel-files-with-asp.net-core-minimal-api-79o45u3s) by [Berkan Sasmaz](https://twitter.com/berkansasmazz) |
|||
* [ABP React Template](https://community.abp.io/posts/abp-react-template-33pjmran) by [Anto Subash](https://twitter.com/antosubash) |
|||
* [Using Dapper with the ABP Framework](https://community.abp.io/posts/using-dapper-with-the-abp-framework-shp74p2l) by [Halil Ibrahim Kalkan](https://twitter.com/hibrahimkalkan) |
|||
* [Converting Create/Edit Modal to Page - Blazor](https://community.abp.io/posts/converting-createedit-modal-to-page-blazor-eexdex8y) by [Enis Necipoglu](https://twitter.com/EnisNecipoglu) |
|||
* [What’s New in .NET 8 🧐 ? Discover ALL .NET 8 Features](https://community.abp.io/posts/whats-new-in-.net-8-discover-all-.net-8-features-llcmrdre) by [Alper Ebicoglu](https://twitter.com/alperebicoglu) |
|||
|
|||
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/articles/submit) to the ABP Community. |
|||
|
|||
### New ABP Blog Posts |
|||
|
|||
There are also some exciting blog posts written by the ABP team. You can see the following list for some of those articles: |
|||
|
|||
* [You Are Invited to ABP .NET Conf’23! 📣](https://blog.abp.io/abp/You-Are-Invited-to-ABP-dotNET-Conf23) by [Bige Beşikçi](https://twitter.com/bigedediki) |
|||
* [💻 Speed Up Your ASP.NET Application 🚀](https://blog.abp.io/abp/Speed-Up-Your-ASP.NET-Application) by [Salih Özkara](https://twitter.com/salihozkara_) |
|||
* [C# 12 🔍 Discover the Exciting New Features & Improvements 🆕🚀](https://blog.abp.io/abp/CSharp-12-Discover-the-Exciting-New-Features-and-Improvements) by [Alper Ebiçoğlu](https://twitter.com/alperebicoglu) |
|||
|
|||
## About the Next Version |
|||
|
|||
The next feature version will be 7.3. You can follow the [release planning here](https://github.com/abpframework/abp/milestones). Please [submit an issue](https://github.com/abpframework/abp/issues/new) if you have any problems with this version. |
|||
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 234 KiB |
@ -0,0 +1,325 @@ |
|||
# Convert Create/Edit Modals to Page |
|||
|
|||
In this document we will explain how to convert the BookStore's Books create & edit modals to regular Angular component pages. |
|||
|
|||
## Before |
|||
|
|||
 |
|||
|
|||
## After |
|||
|
|||
 |
|||
|
|||
# BooksComponent |
|||
|
|||
This is the main component of the books management. The create & update operations are done in this page. So we'll remove the create & update operations from this page and move a separate angular component for each operation. Each component will be a page. |
|||
|
|||
- Remove the Create & Update modal on template |
|||
|
|||
 |
|||
|
|||
- Modify the **NewBook** button with a link to the **CreateBookComponent** page. |
|||
|
|||
```html |
|||
<button |
|||
*abpPermission="'BookStore.Books.Create'" |
|||
id="create" |
|||
class="btn btn-primary" |
|||
type="button" |
|||
[routerLink]="['create']" |
|||
> |
|||
<i class="fa fa-plus me-1"></i> |
|||
<span>{{ '::NewBook' | abpLocalization }}</span> |
|||
</button> |
|||
``` |
|||
|
|||
- Modify the **Edit** button with a link to the **EditBookComponent** page. |
|||
|
|||
```html |
|||
<button |
|||
*abpPermission="'BookStore.Books.Edit'" |
|||
ngbDropdownItem |
|||
[routerLink]="['edit', row.id]" |
|||
> |
|||
{{ '::Edit' | abpLocalization }} |
|||
</button> |
|||
``` |
|||
|
|||
- Remove all unused methods and variables in the `BookComponent` except the **book** variable, the **ngOnInit** and **delete** methods. |
|||
|
|||
|
|||
 |
|||
|
|||
- Also we can clear unncessary imports |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
# CreateBookComponent Page |
|||
|
|||
Create a new component by the name `create-book` in your project. |
|||
```bash |
|||
yarn ng g c book/create-book --skip-tests --style=none |
|||
``` |
|||
|
|||
- `create-book.component.html` |
|||
|
|||
```html |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<form [formGroup]="form" (ngSubmit)="save()"> |
|||
<div class="form-group"> |
|||
<label for="author-id">Author</label><span> * </span> |
|||
<select class="form-control" id="author-id" formControlName="authorId"> |
|||
<option [ngValue]="null">Select author</option> |
|||
<option [ngValue]="author.id" *ngFor="let author of authors$ | async"> |
|||
{{ author.name }} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-name">Name</label><span> * </span> |
|||
<input type="text" id="book-name" class="form-control" formControlName="name" autofocus /> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-price">Price</label><span> * </span> |
|||
<input type="number" id="book-price" class="form-control" formControlName="price" /> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-type">Type</label><span> * </span> |
|||
<select class="form-control" id="book-type" formControlName="type"> |
|||
<option [ngValue]="null">Select a book type</option> |
|||
<option [ngValue]="type.value" *ngFor="let type of bookTypes"> |
|||
{{ '::Enum:BookType.' + type.value | abpLocalization }} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label>Publish date</label><span> * </span> |
|||
<input |
|||
#datepicker="ngbDatepicker" |
|||
class="form-control" |
|||
name="datepicker" |
|||
formControlName="publishDate" |
|||
ngbDatepicker |
|||
(click)="datepicker.toggle()" |
|||
/> |
|||
</div> |
|||
|
|||
<div class="pt-2"> |
|||
<button type="button" class="btn btn-secondary m-1" [routerLink]="['/books']"> |
|||
{{ '::Cancel' | abpLocalization }} |
|||
</button> |
|||
|
|||
<button class="btn btn-primary"> |
|||
<i class="fa fa-check mr-1"></i> |
|||
{{ '::Save' | abpLocalization }} |
|||
</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
``` |
|||
|
|||
- `create-book.component.ts` |
|||
|
|||
```ts |
|||
import { Component, inject } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { map } from 'rxjs'; |
|||
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; |
|||
import { BookService, bookTypeOptions } from '@proxy/books'; |
|||
|
|||
const { required } = Validators; |
|||
|
|||
@Component({ |
|||
selector: 'app-create-book', |
|||
templateUrl: './create-book.component.html', |
|||
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], |
|||
}) |
|||
export class CreateBookComponent { |
|||
//inject() function came with Angular v14, detail: https://angular.io/api/core/inject |
|||
private readonly router = inject(Router); |
|||
private readonly fb = inject(FormBuilder); |
|||
private readonly bookService = inject(BookService); |
|||
|
|||
form: FormGroup; |
|||
authors$ = this.bookService.getAuthorLookup().pipe(map(({ items }) => items)); |
|||
bookTypes = bookTypeOptions; |
|||
|
|||
private buildForm() { |
|||
this.form = this.fb.group({ |
|||
authorId: [null, required], |
|||
name: [null, required], |
|||
type: [null, required], |
|||
publishDate: [undefined, required], |
|||
price: [null, required], |
|||
}); |
|||
} |
|||
|
|||
constructor() { |
|||
this.buildForm(); |
|||
} |
|||
|
|||
save() { |
|||
if (this.form.invalid) return; |
|||
|
|||
this.bookService.create(this.form.value).subscribe(() => { |
|||
this.router.navigate(['/books']); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
# EditBookComponent Page |
|||
|
|||
Create a new component by the name **edit-book** in your project. |
|||
|
|||
```bash |
|||
yarn ng g c book/edit-book --skip-tests --style=none |
|||
``` |
|||
|
|||
- `edit-book.component.html` |
|||
|
|||
```html |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<form *ngIf="form" [formGroup]="form" (ngSubmit)="save()"> |
|||
<div class="form-group"> |
|||
<label for="author-id">Author</label><span> * </span> |
|||
<select class="form-control" id="author-id" formControlName="authorId"> |
|||
<option [ngValue]="null">Select author</option> |
|||
<option [ngValue]="author.id" *ngFor="let author of authors$ | async"> |
|||
{{ author.name }} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-name">Name</label><span> * </span> |
|||
<input type="text" id="book-name" class="form-control" formControlName="name" autofocus /> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-price">Price</label><span> * </span> |
|||
<input type="number" id="book-price" class="form-control" formControlName="price" /> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label for="book-type">Type</label><span> * </span> |
|||
<select class="form-control" id="book-type" formControlName="type"> |
|||
<option [ngValue]="null">Select a book type</option> |
|||
<option [ngValue]="type.value" *ngFor="let type of bookTypes"> |
|||
{{ '::Enum:BookType.' + type.value | abpLocalization }} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<div class="mt-2"> |
|||
<label>Publish date</label><span> * </span> |
|||
<input |
|||
#datepicker="ngbDatepicker" |
|||
class="form-control" |
|||
name="datepicker" |
|||
formControlName="publishDate" |
|||
ngbDatepicker |
|||
(click)="datepicker.toggle()" |
|||
/> |
|||
</div> |
|||
|
|||
<div class="pt-2"> |
|||
<button type="button" class="btn btn-secondary m-1" [routerLink]="['/books']"> |
|||
{{ '::Cancel' | abpLocalization }} |
|||
</button> |
|||
|
|||
<button class="btn btn-primary"> |
|||
<i class="fa fa-check mr-1"></i> |
|||
{{ '::Save' | abpLocalization }} |
|||
</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
``` |
|||
|
|||
- `edit-book.component.ts` |
|||
|
|||
```ts |
|||
import { Component, inject } from '@angular/core'; |
|||
import { ActivatedRoute, Router } from '@angular/router'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { filter, map, switchMap, tap } from 'rxjs'; |
|||
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; |
|||
import { BookDto, BookService, bookTypeOptions } from '@proxy/books'; |
|||
|
|||
const { required } = Validators; |
|||
|
|||
@Component({ |
|||
selector: 'app-edit-book', |
|||
templateUrl: './edit-book.component.html', |
|||
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], |
|||
}) |
|||
export class EditBookComponent { |
|||
//inject() function came with Angular v14, detail: https://angular.io/api/core/inject |
|||
private readonly router = inject(Router); |
|||
private readonly activatedRoute = inject(ActivatedRoute); |
|||
private readonly fb = inject(FormBuilder); |
|||
private readonly bookService = inject(BookService); |
|||
|
|||
id: string; |
|||
form: FormGroup; |
|||
authors$ = this.bookService.getAuthorLookup().pipe(map(({ items }) => items)); |
|||
bookTypes = bookTypeOptions; |
|||
|
|||
private buildForm(book: BookDto): void { |
|||
this.form = this.fb.group({ |
|||
authorId: [book.authorId, required], |
|||
name: [book.name, required], |
|||
type: [book.type, required], |
|||
publishDate: [new Date(book.publishDate), required], |
|||
price: [book.price, required], |
|||
}); |
|||
} |
|||
|
|||
constructor() { |
|||
this.activatedRoute.params |
|||
.pipe( |
|||
filter(params => params.id), |
|||
tap(({ id }) => (this.id = id)), |
|||
switchMap(({ id }) => this.bookService.get(id)), |
|||
tap(book => this.buildForm(book)) |
|||
) |
|||
.subscribe(); |
|||
} |
|||
|
|||
save(): void { |
|||
if (this.form.invalid) return; |
|||
|
|||
this.bookService.update(this.id, this.form.value).subscribe(() => { |
|||
this.router.navigate(['/books']); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
# Update BookRoutingModule |
|||
Finally add 2 items to the routes array in the `book-routing.module.ts` |
|||
```ts |
|||
import { CreateBookComponent } from './create-book/create-book.component'; |
|||
import { EditBookComponent } from './edit-book/edit-book.component'; |
|||
|
|||
const routes: Routes = [ |
|||
{ path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] }, |
|||
{ path: 'create', component: CreateBookComponent }, |
|||
{ path: 'edit/:id', component: EditBookComponent }, |
|||
]; |
|||
``` |
|||
|
|||
|
|||
You can check the following commit for more details: https://github.com/abpframework/abp-samples/commit/351ad5e093036702edbb15169968935496afea0e |
|||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 291 KiB |
|
After Width: | Height: | Size: 678 KiB |
@ -0,0 +1,36 @@ |
|||
# Authority Delegation in ABP Commercial |
|||
|
|||
In this post, I'll explain a new feature that comes with the ABP Commercial `v7.2.0`. It's called **Authority Delegation**. |
|||
|
|||
## Authority Delegation |
|||
|
|||
Authority Delegation is a way of delegating the responsibility of the current user to a different user(s) for a limited time. Thus, the user can switch to the delegated user's account and perform actions on their behalf. |
|||
|
|||
> This feature is part of the [Account Pro module](https://commercial.abp.io/modules/Volo.Account.Pro), which is one of the application PRO modules of [ABP Commercial](https://commercial.abp.io/). |
|||
|
|||
### Delegating a new user |
|||
|
|||
After logging into the application, you can see the `Authority Delegation` menu item under the user menu. When you click the menu, a modal will open, and in the first tab of the modal, you will see the list of delegated users. |
|||
|
|||
 |
|||
|
|||
You can click the `Delegate New User` button to delegate a new user: |
|||
|
|||
 |
|||
|
|||
* You can specify a time range to ensure the delegation is only available within the time range. |
|||
* You can make multiple delegates to the same user and set different delegate time ranges. |
|||
|
|||
> The delegation has three states: `Expired`, `Active`, and `Future`. These states are set automatically by checking the specified time interval. |
|||
|
|||
### My delegated users |
|||
|
|||
A list of users who delegated me to log in on behalf of them can be seen in the figure: |
|||
|
|||
 |
|||
|
|||
You can click the `Login` button to log in to the application as a delegated user and go back to your account by clicking the `Back to my account` icon. |
|||
|
|||
 |
|||
|
|||
> The **Authority Delegation** feature uses the [impersonation system](https://docs.abp.io/en/commercial/latest/modules/account/impersonation) internally. |
|||
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 421 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 247 KiB |
@ -0,0 +1,228 @@ |
|||
# Kubernetes Integrated Microservice Development with .NET and ABP Studio |
|||
|
|||
## Abstract |
|||
|
|||
**Microservice architecture** is a quite common approach to build **highly scalable** solutions with a large development team. While there are standard tools, like **Kubernetes**, to deploy, run and scale your microservices, the **development side** is not as mature as that. It is not easy to develop, run and test a single microservice that **depends on** other microservices and services. **Running** a copy of the entire system in the developer's machine is impractical. |
|||
|
|||
In this article, I will introduce an **efficient** way of creating a **development environment** that is well **integrated to Kubernetes**. In this way, you can just run the microservice you are building and let Kubernetes run all your dependencies in your local machine or a remote server. |
|||
|
|||
I will demonstrate the solution by introducing and using a brand new tool for ABP Developers: **ABP Studio**! |
|||
|
|||
>**This article is based on my *[Kubernetes Integrated Microservice Development with ABP Studio](https://www.youtube.com/watch?v=XiPRcIHJ3NE)* talk performed in [ABP Dotnet Conf'23](https://abp.io/conference). You can watch that talk if you would like watching rather than reading.** |
|||
|
|||
## Topics |
|||
|
|||
Let’s start by introducing the main topics I will cover in this article: |
|||
|
|||
* I will begin by defining the **problem**: Why it is hard to prepare a development environment for a microservice development. |
|||
* I will mention and demonstrate how **Microsoft’s Tye project** tries to solve the problem and why it falls short. |
|||
* Then I will introduce the **ABP Studio**, a brand new product for .NET developers, which has an efficient solution for **running multi-application solutions** locally. |
|||
* Finally, I will offer an efficient way of developing such complex solutions locally by **integrating Kubernetes into our development environment**, and demonstrate how ABP Studio automates it for us. |
|||
|
|||
## Difficulties of a Microservice Development Environment |
|||
|
|||
It is important to understand what is the essential problem when we try to set up a development environment for a microservice solution. To understand the main difficulty, let’s see what a microservice solution looks like: |
|||
|
|||
 |
|||
|
|||
A typical microservice solution consists of many components communicating to each other. In this example; |
|||
|
|||
* We have **two applications** for users. One for back-office admin users and one for the end-users of the system, a public website. |
|||
* These applications use dedicated **API Gateways** since we are using the Backend For Frontend pattern here. |
|||
* There is a separate **authentication service** application to login and manage user accounts. |
|||
* Behind the API gateways, we typically have many API applications, called **microservices**. Here, I show only three microservices, but there will be tens of microservices even in a medium-size system. Each of these microservices may have their own databases. |
|||
* The microservices typically **interact** with each other using REST API calls or asynchronous messaging through an event bus service, like RabbitMQ here. |
|||
|
|||
There will be much more **supporting services**, like Redis, Elasticsearch and so on. As you see, we have a lot of independent services and applications interacting with each other in a typical microservice solution. |
|||
|
|||
### How to run a single service locally? |
|||
|
|||
Generally, your responsibility is developing **one or a few of these microservices** or applications, not all of them. For example, you may be responsible for developing or testing the Product microservice: |
|||
|
|||
 |
|||
|
|||
All you want to do is to write your code, then run and test your service if it is correctly working. However, to be able to **develop**, **run**, **test** or **debug** the product microservice, all of the service and infrastructure dependencies need to be running and communicating to each other. |
|||
|
|||
Okay, maybe not all, but most of them are needed. Otherwise, you can not open the application UI, login to the system, add products to basket and place an order to see if it is working properly, or you can not **live debug** your service code in case of trouble. |
|||
|
|||
So, how to make the whole system up and running easily to be able to **focus on developing**, testing and debugging the Product microservice? |
|||
|
|||
## The Project Tye |
|||
|
|||
You are not the only one getting trouble to develop, run, test and debug a microservice solution. Microsoft also thought of it and introduced a tool called the [Project Tye](https://github.com/dotnet/tye). With its own words in their GitHub repository: |
|||
|
|||
> Tye is a tool that makes developing, testing, and deploying microservices and distributed applications easier. |
|||
|
|||
Tye can **simplify microservices development** by making it easy to run many services with one command, use dependencies in containers, discover addresses of other services using simple conventions. It can also deploy .NET applications to Kubernetes by automatically creating containers for .NET applications and generating Kubernetes manifests with minimal knowledge or configuration. |
|||
|
|||
The deployment part is not mature and they have to do a lot of work. However, the solution runner part works very well. |
|||
|
|||
Tye project consists of three major components: |
|||
|
|||
* A **YAML file** to configure your services |
|||
* A **CLI tool** to run and deploy the solution |
|||
* And a **dashboard** to visualize the running services. |
|||
|
|||
### The tye.yaml file |
|||
|
|||
Tye uses a YAML file to configure your solution. It typically contains many service definitions. There are three type of services can be defined in this YAML file: |
|||
|
|||
* You can add a **.NET Project**, so Tye runs it locally with **dotnet run** |
|||
* You can add a **container definition**, and Tye runs it using **Docker** |
|||
* And an arbitrary **executable application** that can be run using a terminal command |
|||
|
|||
Here, an example content of a `tye.yaml` file: |
|||
|
|||
 |
|||
|
|||
### The Tye CLI |
|||
|
|||
Once you define your YAML file, Tye can run all the services with a single terminal command: **tye run**. It builds all .NET applications before running them. If you specify the **watch parameter**, it watches the changes in your .NET projects, re-builds and runs them when a change happens. There are also [other commands](https://github.com/dotnet/tye/blob/main/docs/reference/commandline/README.md) to build and deploy your solution. |
|||
|
|||
### The Tye Dashboard |
|||
|
|||
The final major component is the Tye Dashboard. With this dashboard, you can see the **list of the services** with their statuses. You can also view their **logs** and open their **UI** with a single click. |
|||
|
|||
A screenshot from the Tye Dashboard: |
|||
|
|||
 |
|||
|
|||
### The Example Microservice Solution |
|||
|
|||
I’ve prepared a microservice solution for demos in this article and I will use the same solution in all demos. Here, an overall diagram of the example solution: |
|||
|
|||
 |
|||
|
|||
We have two web applications, two API Gateways, 5 microservices. 3 of them are shown here: Product, Ordering and Identity microservices. Every microservice has its independent SQL Server database. They communicate through REST API calls and distributed events via RabbitMQ. |
|||
|
|||
### The Project Tye Demo |
|||
|
|||
You can watch my 6-minutes demo to see the Project Tye in action: |
|||
|
|||
<iframe width="560" height="315" src="https://www.youtube.com/embed/S0-z29lMokA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> |
|||
|
|||
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=S0-z29lMokA) |
|||
|
|||
### Tye: Shortcomings |
|||
|
|||
The demo shows how the Tye project is useful. However, there are many missing points and problems with the current project state. Let’s talk about them; |
|||
|
|||
* The first problem is that it **starts very slow**, because every time you run it, it builds all the services. It takes more than 1 minute for a solution with only ten services. |
|||
* It currently can run everything in your **local computer**. Even if you want to develop a single service, running tens of services locally consumes your system resources. You need a lot of RAM and CPU if you want to work a large solution locally. |
|||
* The **UI part** is currently very simple. For example, if you want to debug one of your services by running it in Visual Studio, you need to stop all the services, change the YAML file to exclude that service from Tye, then run all the services again. Only after that you can run your service in Visual Studio to debug it as integrated to other services. Obviously this process is not easy and comfortable. |
|||
* We can only see the application logs in the UI. **More insights** about the internals of services would be very helpful. |
|||
* Tye currently has some **partial documents** in its GitHub repository. However, the documentation is far away from being complete. |
|||
* Finally, and most importantly, there is **no active development** on the Tye project on GitHub. It was already an experimental project with its early state, as declared in its GitHub repository. |
|||
|
|||
So, basically, it seems Microsoft has no further interest in this project and you can expect it will be retired in the near future. |
|||
|
|||
## ABP Studio |
|||
|
|||
Okay, now we came to the ABP Studio part. At [Volosoft](https://www.volosoft.com/), we were silently working on the ABP Studio project for more than one year. |
|||
|
|||
ABP Studio is a **cross-platform desktop application** for ABP developers. It is **well integrated** to the ABP Framework and aims to provide a comfortable development environment for you by **automating things**, **providing insights** about your solution, making **develop**, **run** and **deploy** your solutions much easier. |
|||
|
|||
We are planning to release a **beta version** in **Q3 of 2023**. You can expect the following features shipping with the initial release: |
|||
|
|||
* You can **create new ABP solutions** and modules easily with a lot of options. |
|||
* You can easily **install or uninstall modules** to your solution. |
|||
* You can **explore** the fundamental **structures** of your solution or used modules, like entities, repositories, application services, UI pages, HTTP APIs, database tables and much more. |
|||
* You can build **multi-module monolith applications** or distributed microservice solutions by easily adding modules and services into your solution. |
|||
* You can easily **run multiple applications** and services with a single click, just like I demonstrated with the Project Tye. However, it has much more features. |
|||
* You can run your service by **integrating** it into a **Kubernetes** cluster, so you don’t need to run all your dependencies and all other services in your local development environment. This is the essential topic of this talk and I will later demonstrate how it works. |
|||
|
|||
## ABP Studio Solution Runner |
|||
|
|||
I've prepared a demo of the ABP Studio Solution Runner, but first I want to mention the main features of the solution runner: |
|||
|
|||
* First of all, it can run one, **multiple** or **all services** with a single click. In this way, it is very easy to stop a service, run it in Visual Studio to **test** or **debug**. |
|||
* All the services are **connected to ABP Studio** and send their internal data to **visualize** on the ABP Studio UI. |
|||
* In the **overall view**, you can see a list of services, view **real-time** HTTP Request and exception counts for each service. |
|||
* You can see all details of all **HTTP requests** coming to any service. |
|||
* You can see **exception details** as real-time in any service, easily filter and search. |
|||
* Just like Tye, you can see the **application logs**. But as more, you can filter logs by log level or search in the log texts. |
|||
* Last but not least, ABP Studio has an **integrated Chrome browser** inside it. You can browse the UI of your application without leaving the solution runner. |
|||
|
|||
### ABP Studio Solution Runner Demo |
|||
|
|||
You can watch my 9-minutes demo to see ABP Studio Solution Runner in action: |
|||
|
|||
<iframe width="560" height="315" src="https://www.youtube.com/embed/sSCxyccoHqE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> |
|||
|
|||
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=sSCxyccoHqE) |
|||
|
|||
## ABP Studio Kubernetes Tunnel |
|||
|
|||
Finally, we came to the essential topic I want to talk about. Until that point, we had a good understanding of the problem and possible solutions. In this part, we will make a final touch to the solution to have **a great development environment for a microservice solution**. |
|||
|
|||
ABP Studio Kubernetes Tunnel System allows you to **connect your local development environment to a local or remote Kubernetes cluster**, where that cluster already runs your microservice solution. In this way, you can access any service in Kubernetes with their service name as DNS, just like they are running in your local computer. This is established with a secure VPN connection. |
|||
|
|||
Secondly, you can **intercept any service** in that cluster, so all the **traffic to the intercepted service is automatically redirected to your service** that is running in your local machine. When your service needs to use any service in Kubernetes, the traffic is redirected back to the cluster, just like your local service is running inside the Kubernetes. In this way, you don’t need to care about how all other services are configured and running. You just focus on the service you are responsible for developing, testing or debugging. You can use your favorite IDE since you are running your service in your local machine as you always do. |
|||
|
|||
The solution runner is a great way of running multiple services locally. However, if your solution consists of hundreds of services, running all them in your local machine will consume your system resources and slow down your development speed. The best news is that: You can use the **Kubernetes Tunnelling combined with all the solution runner features** to have a perfect local microservice development environment. |
|||
|
|||
### How ABP Studio Kubernetes Tunnel works |
|||
|
|||
I am sure that you want to see it in action, but before that, let me explain how the solution works. No problem if you can not understand it completely. It is enough to see the big picture: |
|||
|
|||
 |
|||
|
|||
Let's explain how it works: |
|||
|
|||
* **Kubernetes cluster** is shown on the right side and your **local development machine** is shown on the left side. |
|||
* As you know, when a **user requests a web page** from your web application, the request is accepted by an **Ingress Controller** inside your Kubernetes cluster. |
|||
* The Ingress controller forwards the request to your **web application**, which then uses an **API gateway** to consume your microservices. |
|||
* Assume that we have the **Product**, **Ordering** and **Identity** microservices. |
|||
* These microservices have their own SQL Server **database** and they are also using **RabbitMQ**, **Redis** and some other **infrastructure services**. |
|||
* On the other hand, we want to develop, run or test our **Product microservice** in our **own laptop** using our favorite IDE, let’s say using Visual Studio. |
|||
* If we don’t make anything special, the **product microservice in the Kubernetes cluster** will be used by the users, as you can expect. Even if you can somehow run the product microservice in your local computer, the Kubernetes system won’t have any knowledge about it. They are in different systems. |
|||
* At this point, ABP Studio comes into play. When you **connect** to the Kubernetes cluster with ABP Studio, it first installs a **VPN server** into your cluster and a **VPN client** into your local machine. |
|||
* Then it establishes a secure **VPN tunnel** between your computer and the target Kubernetes cluster. In this way, you can access all services in Kubernetes with their internal cluster IP addresses. |
|||
* ABP Studio also installs the **ABP Studio Client Proxy Server** pod into the Kubernetes cluster, and the **ABP Studio Proxy Client** into the developer machine. It is used to collect data from the services and show them in the ABP Studio UI, like HTTP Requests, exceptions, or logs as we’ve seen in the solution runner before. |
|||
* Finally, ABP Studio also adds **DNS records to your hosts file**, so you can access to internal Kubernetes services directly with their service names, in addition to their IP addresses. |
|||
|
|||
All of these happen when you click the Connect button on the ABP Studio. Now, your computer can use the **internals of the Kubernetes cluster**. But, how the HTTP Requests coming to the Product service in the Kubernetes cluster are **redirected to your local machine**? |
|||
|
|||
* When you want to run and test a service locally, you **intercept the matched service** in the Kubernetes cluster. When you Intercept a service, ABP Studio makes some more changes in your local computer and in your Kubernetes cluster. |
|||
* As first, it installs a **pod** and a **service** into the Kubernetes cluster to intercept the requests coming to the Product microservice and **proxies the requests to** a client application in **your local computer**. |
|||
* The local client application then **forwards requests** to the Product microservice instance you are running in your local machine. In this way, whenever the Product service is used in Kubernetes, your local product service is executed. You can easily run, test or debug your service as you normally do with your standard IDE, for example with Visual Studio. |
|||
* What happens when your local product service needs to **access** the SQL Server database or RabbitMQ, or another microservice in the Kubernetes cluster? It can **directly use** them just like local services, through the **VPN tunnel** we have already created. |
|||
* ABP Studio makes a final change in your local product microservice project: It overrides all the **environment variables** with the ones obtained from the product service in the Kubernetes pod. In this way, your local product service instance feels itself in the Kubernetes cluster. Database connection strings and all other settings will be the same with the Kubernetes environment. |
|||
|
|||
Don’t worry, ABP Studio **doesn’t change your local environment variables**. The change only affects the live product service instance, and it is **reverted** when you disable the interception in ABP Studio. Also, when you click the **Disconnect** button in ABP Studio, all the changes made to your cluster and your local computer will be cleared. It won’t leave any sign in your systems. |
|||
|
|||
### ABP Studio Kubernetes Tunnel Demo |
|||
|
|||
So, now we have a fully Kubernetes integrated development environment. Let’s see how it is used in ABP Studio in the following 9-minutes demo: |
|||
|
|||
<iframe width="560" height="315" src="https://www.youtube.com/embed/CeUq2ysz-mQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> |
|||
|
|||
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=CeUq2ysz-mQ) |
|||
|
|||
## ABP Studio Roadmap |
|||
|
|||
In the next months, the team will be working on the following items: |
|||
|
|||
* Our first goal is to **finalize and stabilize** the current features. We will also **document** how to use them. Only after that, we will open it to customers as a beta version. |
|||
* We’ve focused on the development part until now. We also want to add some **deployment** capabilities to ABP Studio in the future. |
|||
* Another goal is to make **module customizations** much easier than current by the help of ABP Studio. With ABP Studio, we have completely re-though how to discover and install modules. Based on that, we will also work on a common place to publish and consume application modules for the ABP Framework. |
|||
|
|||
We have other great plans for that product. However, I can not declare them yet as they are secrets for now :) We’ve planned to release the first beta version in **Q3 of 2023**. |
|||
|
|||
I want to notice that ABP Studio is not a new version or replacement of **ABP Suite**. We built the ABP Studio from scratch. Both products will be developed and shipped in parallel. ABP Suite will continue to focus on code generation. ABP Studio doesn’t have any code generation capabilities yet. So, you will use both of them together. |
|||
|
|||
## Summary |
|||
|
|||
Okay let’s summarize what I’ve explained in this article: |
|||
|
|||
* First, I explained **why it is hard to set up a microservice development environment**. We’ve talked about the hardness of developing, running and testing a single microservice that depends on infrastructure services and other microservices. |
|||
* I’ve introduced **Project Tye**, a project developed by Microsoft to allow you to easily run multi-application solutions. We made a demo, then discussed its missing points. |
|||
* Then I **introduced ABP Studio**, a new product by Volosoft for ABP developers. |
|||
As the first important feature, I explained the ABP Studio **Solution Runner** and demonstrated how it works and how you can develop and debug your services. |
|||
* Finally, I introduced ABP Studio’s **Kubernetes Tunneling** feature. First, I explained how it works with a complicated diagram. Then I made a short demo to see it in action. |
|||
|
|||
With that final part, we had a **great and easy to use microservice development environment**. It is great, because we can focus on only a single service, while we don’t care about all other parts of the solution. Also, we can save our local development machine system resources by delegating running the dependencies to a remote Kubernetes server. |
|||
|
|||
## See Also |
|||
|
|||
* ABP Dotnet Conf'23 Talk on YouTube: [Kubernetes Integrated Microservice Development with ABP Studio](https://www.youtube.com/watch?v=XiPRcIHJ3NE) |
|||
@ -0,0 +1,380 @@ |
|||
# Image Manipulation |
|||
ABP Framework provides services to compress and resize images and implements these services with popular [ImageSharp](https://sixlabors.com/products/imagesharp/) and [Magick.NET](https://github.com/dlemstra/Magick.NET) libraries. You can use these services in your reusable modules, libraries and applications, so you don't depend on a specific imaging library. |
|||
|
|||
> The image resizer/compressor system is designed to be extensible. You can implement your own image resizer/compressor contributor and use it in your application. |
|||
|
|||
## Installation |
|||
|
|||
You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach. |
|||
|
|||
### Using the ABP CLI |
|||
|
|||
Open a command line terminal in the folder of your project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.Imaging.Abstractions |
|||
``` |
|||
|
|||
### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.Imaging.Abstractions](https://www.nuget.org/packages/Volo.Abp.Imaging.Abstractions) NuGet package to your project: |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.Imaging.Abstractions |
|||
``` |
|||
|
|||
2. Add the `AbpImagingAbstractionsModule` to the dependency list of your module: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
//...other dependencies |
|||
typeof(AbpImagingAbstractionsModule) //Add the new module dependency |
|||
)] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
## Providers |
|||
|
|||
ABP Framework provides two image resizer/compressor implementations out of the box: |
|||
|
|||
* [Magick.NET](#magick-net-provider) |
|||
* [ImageSharp](#imagesharp-provider) |
|||
|
|||
You should install one of these provides to make it actually working. |
|||
|
|||
> If none of the provider packages installed into your application, compress/resize operations return the untouched input image. |
|||
|
|||
## IImageResizer |
|||
|
|||
You can [inject](Dependency-Injection.md) the `IImageResizer` service and use it for image resize operations. Here is the available methods of the `IImageResizer` service: |
|||
|
|||
```csharp |
|||
public interface IImageResizer |
|||
{ |
|||
/* Works with a Stream object that represents an image */ |
|||
Task<ImageResizeResult<Stream>> ResizeAsync( |
|||
Stream stream, |
|||
ImageResizeArgs resizeArgs, |
|||
string mimeType = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/* Works with a byte array that contains an image file */ |
|||
Task<ImageResizeResult<byte[]>> ResizeAsync( |
|||
byte[] bytes, |
|||
ImageResizeArgs resizeArgs, |
|||
string mimeType = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
``` |
|||
|
|||
**Example usage:** |
|||
|
|||
```csharp |
|||
var result = await _imageResizer.ResizeAsync( |
|||
stream, /* A stream object that represents an image */ |
|||
new ImageResizeArgs |
|||
{ |
|||
Width = 100, |
|||
Height = 100, |
|||
Mode = ImageResizeMode.Crop |
|||
}, |
|||
mimeType: "image/jpeg" |
|||
); |
|||
``` |
|||
|
|||
> You can use `MimeTypes.Image.Jpeg` constant instead of the `image/jpeg` magic string used in that example. |
|||
|
|||
### ImageResizeArgs |
|||
|
|||
The `ImageResizeArgs` is a class that is used to define the resize operation parameters. It has the following properties: |
|||
|
|||
* `Width`: The width of the resized image. |
|||
* `Height`: The height of the resized image. |
|||
* `Mode`: The resize mode (see the [ImageResizeMode](#imageresizemode) section for more information). |
|||
|
|||
### ImageResizeMode |
|||
|
|||
The `ImageResizeMode` is an enum that is used to define the resize mode. It has the following values: |
|||
|
|||
```csharp |
|||
public enum ImageResizeMode : byte |
|||
{ |
|||
None = 0, |
|||
Stretch = 1, |
|||
BoxPad = 2, |
|||
Min = 3, |
|||
Max = 4, |
|||
Crop = 5, |
|||
Pad = 6, |
|||
Default = 7 |
|||
} |
|||
``` |
|||
|
|||
> See the [ImageSharp documentation](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Processing.ResizeMode.html) for more information about the resize modes. |
|||
|
|||
### ImageResizeResult |
|||
|
|||
The `ImageResizeResult` is a generic class that is used to return the result of the image resize operations. It has the following properties: |
|||
|
|||
* `Result`: The resized image (stream or byte array). |
|||
* `State`: The result of the resize operation (type: `ImageProcessState`). |
|||
|
|||
### ImageProcessState |
|||
|
|||
The `ImageProcessState` is an enum that is used to return the the result of the image resize operations. It has the following values: |
|||
|
|||
```csharp |
|||
public enum ImageProcessState : byte |
|||
{ |
|||
Done = 1, |
|||
Canceled = 2, |
|||
Unsupported = 3, |
|||
} |
|||
``` |
|||
|
|||
### ImageResizeOptions |
|||
|
|||
`ImageResizeOptions` is an [options object](Options.md) that is used to configure the image resize system. It has the following properties: |
|||
|
|||
* `DefaultResizeMode`: The default resize mode. (Default: `ImageResizeMode.None`) |
|||
|
|||
## IImageCompressor |
|||
|
|||
You can [inject](Dependency-Injection.md) the `IImageCompressor` service and use it for image compression operations. Here is the available methods of the `IImageCompressor` service: |
|||
|
|||
```csharp |
|||
public interface IImageCompressor |
|||
{ |
|||
/* Works with a Stream object that represents an image */ |
|||
Task<ImageCompressResult<Stream>> CompressAsync( |
|||
Stream stream, |
|||
string mimeType = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/* Works with a byte array that contains an image file */ |
|||
Task<ImageCompressResult<byte[]>> CompressAsync( |
|||
byte[] bytes, |
|||
string mimeType = null, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
``` |
|||
|
|||
**Example usage:** |
|||
|
|||
```csharp |
|||
var result = await _imageCompressor.CompressAsync( |
|||
stream, /* A stream object that represents an image */ |
|||
mimeType: "image/jpeg" |
|||
); |
|||
``` |
|||
|
|||
### ImageCompressResult |
|||
|
|||
The `ImageCompressResult` is a generic class that is used to return the result of the image compression operations. It has the following properties: |
|||
|
|||
* `Result`: The compressed image (stream or byte array). |
|||
* `State`: The result of the compress operation (type: `ImageProcessState`). |
|||
|
|||
### ImageProcessState |
|||
|
|||
The `ImageProcessState` is an enum that is used to return the the result of the image compress operations. It has the following values: |
|||
|
|||
```csharp |
|||
public enum ImageProcessState : byte |
|||
{ |
|||
Done = 1, |
|||
Canceled = 2, |
|||
Unsupported = 3, |
|||
} |
|||
``` |
|||
|
|||
## Magick.NET Provider |
|||
|
|||
`Volo.Abp.Imaging.MagickNet` NuGet package implements the image operations using the [Magick.NET](https://github.com/dlemstra/Magick.NET) library. |
|||
|
|||
## Installation |
|||
|
|||
You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach. |
|||
|
|||
### Using the ABP CLI |
|||
|
|||
Open a command line terminal in the folder of your project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.Imaging.MagickNet |
|||
``` |
|||
|
|||
### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.Imaging.MagickNet](https://www.nuget.org/packages/Volo.Abp.Imaging.MagickNet) NuGet package to your project: |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.Imaging.MagickNet |
|||
``` |
|||
|
|||
2. Add `AbpImagingMagickNetModule` to your [module](Module-Development-Basics.md)'s dependency list: |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpImagingMagickNetModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### Configuration |
|||
|
|||
`MagickNetCompressOptions` is an [options object](Options.md) that is used to configure the Magick.NET image compression system. It has the following properties: |
|||
|
|||
* `OptimalCompression`: Indicates whether the optimal compression is enabled or not. (Default: `false`) |
|||
* `IgnoreUnsupportedFormats`: Indicates whether the unsupported formats are ignored or not. (Default: `false`) |
|||
* `Lossless`: Indicates whether the lossless compression is enabled or not. (Default: `false`) |
|||
|
|||
## ImageSharp Provider |
|||
|
|||
`Volo.Abp.Imaging.ImageSharp` NuGet package implements the image operations using the [ImageSharp](https://github.com/SixLabors/ImageSharp) library. |
|||
|
|||
## Installation |
|||
|
|||
You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach. |
|||
|
|||
### Using the ABP CLI |
|||
|
|||
Open a command line terminal in the folder of your project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.Imaging.ImageSharp |
|||
``` |
|||
|
|||
### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.Imaging.ImageSharp](https://www.nuget.org/packages/Volo.Abp.Imaging.ImageSharp) NuGet package to your project: |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.Imaging.ImageSharp |
|||
``` |
|||
|
|||
2. Add `AbpImagingImageSharpModule` to your [module](Module-Development-Basics.md)'s dependency list: |
|||
|
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpImagingImageSharpModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### Configuration |
|||
|
|||
`ImageSharpCompressOptions` is an [options object](Options.md) that is used to configure the ImageSharp image compression system. It has the following properties: |
|||
|
|||
* `DefaultQuality`: The default quality of the JPEG and WebP encoders. (Default: `75`) |
|||
* [`JpegEncoder`](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder.html): The JPEG encoder. (Default: `JpegEncoder` with `Quality` set to `DefaultQuality`) |
|||
* [`PngEncoder`](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Formats.Png.PngEncoder.html): The PNG encoder. (Default: `PngEncoder` with `IgnoreMetadata` set to `true` and `CompressionLevel` set to `PngCompressionLevel.BestCompression`) |
|||
* [`WebPEncoder`](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Formats.Webp.WebpEncoder.html): The WebP encoder. (Default: `WebPEncoder` with `Quality` set to `DefaultQuality`) |
|||
|
|||
**Example usage:** |
|||
|
|||
```csharp |
|||
Configure<ImageSharpCompressOptions>(options => |
|||
{ |
|||
options.JpegEncoder = new JpegEncoder |
|||
{ |
|||
Quality = 60 |
|||
}; |
|||
options.PngEncoder = new PngEncoder |
|||
{ |
|||
CompressionLevel = PngCompressionLevel.BestCompression |
|||
}; |
|||
options.WebPEncoder = new WebPEncoder |
|||
{ |
|||
Quality = 65 |
|||
}; |
|||
}); |
|||
``` |
|||
|
|||
## ASP.NET Core Integration |
|||
|
|||
`Volo.Abp.Imaging.AspNetCore` NuGet package defines attributes for controller actions that can automatically compress and/or resize uploaded files. |
|||
|
|||
## Installation |
|||
|
|||
You can add this package to your application by either using the [ABP CLI](CLI.md) or manually installing it. Using the [ABP CLI](CLI.md) is the recommended approach. |
|||
|
|||
### Using the ABP CLI |
|||
|
|||
Open a command line terminal in the folder of your project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.Imaging.AspNetCore |
|||
``` |
|||
|
|||
### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.Imaging.AspNetCore](https://www.nuget.org/packages/Volo.Abp.Imaging.AspNetCore) NuGet package to your project: |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.Imaging.AspNetCore |
|||
``` |
|||
|
|||
2. Add `AbpImagingAspNetCoreModule` to your [module](Module-Development-Basics.md)'s dependency list: |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpImagingAspNetCoreModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### CompressImageAttribute |
|||
|
|||
The `CompressImageAttribute` is used to compress the image before. `IFormFile`, `IRemoteStreamContent`, `Stream` and `IEnumrable<byte>` types are supported. It has the following properties: |
|||
|
|||
* `Parameters`: Names of the the parameters that are used to configure the image compression system. This is useful if your action has some non-image parameters. If you don't specify the parameters names, all of the method parameters are considered as image. |
|||
|
|||
**Example usage:** |
|||
|
|||
```csharp |
|||
[HttpPost] |
|||
[CompressImage] /* Compresses the given file (automatically determines the file mime type) */ |
|||
public async Task<IActionResult> Upload(IFormFile file) |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
### ResizeImageAttribute |
|||
|
|||
The `ResizeImageAttribute` is used to resize the image before requesting the action. `IFormFile`, `IRemoteStreamContent`, `Stream` and `IEnumrable<byte>` types are supported. It has the following properties: |
|||
|
|||
* `Parameters`: Names of the the parameters that are used to configure the image resize system. This is useful if your action has some non-image parameters. If you don't specify the parameters names, all of the method parameters are considered as image. |
|||
* `Width`: Target width of the resized image. |
|||
* `Height`: Target height of the resized image. |
|||
* `Mode`: The resize mode (see the [ImageResizeMode](#imageresizemode) section for more information). |
|||
|
|||
**Example usage:** |
|||
|
|||
```csharp |
|||
[HttpPost] |
|||
[ResizeImage(Width = 100, Height = 100, Mode = ImageResizeMode.Crop)] |
|||
public async Task<IActionResult> Upload(IFormFile file) |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
@ -0,0 +1,28 @@ |
|||
# ABP Version 7.3 Migration Guide |
|||
|
|||
This document is a guide for upgrading ABP v7.2 solutions to ABP v7.3. There are a few changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. |
|||
|
|||
## OpenIddict - Refactoring of `ClaimsPrincipal` |
|||
|
|||
There are some changes that you might need to fix in your code. You can see the following list of the required changes: |
|||
|
|||
* `AbpOpenIddictClaimDestinationsManager` was renamed as `AbpOpenIddictClaimsPrincipalManager`. |
|||
* Use `AbpOpenIddictClaimsPrincipalManager.HandleAsync` instead of `AbpOpenIddictClaimDestinationsManager.SetAsync`, which is removed. |
|||
* `AbpDefaultOpenIddictClaimDestinationsProvider` was renamed as `AbpDefaultOpenIddictClaimsPrincipalHandler`. |
|||
* `IAbpOpenIddictClaimDestinationsProvider` was renamed as `IAbpOpenIddictClaimsPrincipalHandler`. |
|||
* Use `IAbpOpenIddictClaimsPrincipalHandler.HandleAsync` instead of `IAbpOpenIddictClaimDestinationsProvider.SetAsync`, which is removed. |
|||
* `AbpOpenIddictClaimDestinationsOptions` was renamed as `AbpOpenIddictClaimsPrincipalOptions`. |
|||
|
|||
Please check [this PR](https://github.com/abpframework/abp/pull/16537) if you encounter any problems related to OpenIddict Module. |
|||
|
|||
## Nonce attribute support for Content Security Policy (CSP) |
|||
|
|||
ABP Framework supports adding unique value to nonce attribute for script tags which can be used by Content Security Policy to determine whether or not a given fetch will be allowed to proceed for a given element. In other words, it provides a mechanism to execute only correct script tags with the correct nonce value. |
|||
|
|||
> See the [Security Headers](../UI/AspNetCore/Security-Headers.md) documentation for more information. |
|||
|
|||
This feature comes with a small restriction. If you use any C# code used inside the script tag, it may cause errors (Because a new `NonceScriptTagHelper` has been added, and it replaces script tags in the HTML contents). |
|||
|
|||
For example, `<script @string.Empty></script>` will no longer work. However, you can use the C# code for an attribute of script tag, for example, `<script src="@string.Empty"></script>` is completely valid and won't cause any problem. |
|||
|
|||
> Note: You should not use any C# code used inside the script tag, even if you don't use this feature. Because it might cause errors. |
|||
@ -0,0 +1,51 @@ |
|||
# Checkbox Component |
|||
|
|||
The ABP Checkbox Component is a reusable form input component for the checkbox type. |
|||
|
|||
# Inputs |
|||
|
|||
- `label` |
|||
- `labelClass (default form-check-label)` |
|||
- `checkboxId` |
|||
- `checkboxReadonly` |
|||
- `checkboxReadonly (default form-check-input)` |
|||
- `checkboxStyle` |
|||
|
|||
# Outputs |
|||
|
|||
- `checkboxBlur` |
|||
- `checkboxFocus` |
|||
|
|||
# Usage |
|||
|
|||
The ABP Checkbox component is a part of the `ThemeSharedModule` module. If you've imported that module into your module, there's no need to import it again. If not, then first import it as shown below: |
|||
|
|||
```ts |
|||
// my-feature.module.ts |
|||
|
|||
import { ThemeSharedModule } from "@abp/ng.theme.shared"; |
|||
import { CheckboxDemoComponent } from "./CheckboxDemoComponent.component"; |
|||
|
|||
@NgModule({ |
|||
imports: [ |
|||
ThemeSharedModule, |
|||
// ... |
|||
], |
|||
declarations: [CheckboxDemoComponent], |
|||
// ... |
|||
}) |
|||
export class MyFeatureModule {} |
|||
``` |
|||
|
|||
Then, the `abp-checkbox` component can be used. See the example below: |
|||
|
|||
```html |
|||
<div class="form-check"> |
|||
<abp-checkbox label="Yes,I Agree" checkboxId="checkbox-input"> |
|||
</abp-checkbox> |
|||
</div> |
|||
``` |
|||
|
|||
See the checkbox input result below: |
|||
|
|||
 |
|||
@ -0,0 +1,49 @@ |
|||
# Form Input Component |
|||
|
|||
The ABP FormInput Component is a reusable form input component for the text type. |
|||
|
|||
# Inputs |
|||
* `label` |
|||
* `labelClass (default form-label)` |
|||
* `inputPlaceholder` |
|||
* `inputReadonly` |
|||
* `inputClass (default form-control)` |
|||
|
|||
# Outputs |
|||
* `formBlur` |
|||
* `formFocus` |
|||
|
|||
# Usage |
|||
|
|||
The ABP FormInput component is a part of the `ThemeSharedModule` module. If you've imported that module into your module, there's no need to import it again. If not, then first import it as shown below: |
|||
|
|||
```ts |
|||
import { ThemeSharedModule } from "@abp/ng.theme.shared"; |
|||
import { FormInputDemoComponent } from "./FomrInputDemoComponent.component"; |
|||
|
|||
@NgModule({ |
|||
imports: [ |
|||
ThemeSharedModule, |
|||
// ... |
|||
], |
|||
declarations: [FormInputDemoComponent], |
|||
}) |
|||
export class MyFeatureModule {} |
|||
``` |
|||
|
|||
Then, the `abp-form-input` component can be used. See the example below: |
|||
|
|||
```html |
|||
<div class="row"> |
|||
<div class="col-4"> |
|||
<abp-form-input |
|||
label="AbpAccount::UserNameOrEmailAddress" |
|||
inputId="login-input-user-name-or-email-address" |
|||
></abp-form-input> |
|||
</div> |
|||
</div> |
|||
``` |
|||
|
|||
See the form input result below: |
|||
|
|||
 |
|||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1,99 @@ |
|||
# Security Headers |
|||
|
|||
ABP Framework allows you to add frequently used security headers into your application. The following security headers will be added as response headers to your application if you use the `UseAbpSecurityHeaders` middleware: |
|||
|
|||
* `X-Content-Type-Options`: Tells the browser to not try and guess what a mime-type of a resource might be, and to just take what mime-type the server has returned. |
|||
* `X-XSS-Protection`: This is a feature of Internet Explorer, Chrome, and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. |
|||
* `X-Frame-Options`: This header can be used to indicate whether or not a browser should be allowed to render a page in a `<iframe>` tag. By specifying this header value as *SAMEORIGIN*, you can make it displayed in a frame on the same origin as the page itself. |
|||
* `Content-Security-Policy`: This response header allows you to restrict which resources (such as JavaScript, CSS, images, manifests, etc.) can be loaded, and the URLs that they can be loaded from. This security header will only be added if you configure the `AbpSecurityHeadersOptions` class and enable it. |
|||
|
|||
## Configuration |
|||
|
|||
### AbpSecurityHeadersOptions |
|||
|
|||
`AbpSecurityHeadersOptions` is the main class to enable the `Content-Security-Policy` header, define its value and set other security headers that you want to add to your application. |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
Configure<AbpSecurityHeadersOptions>(options => |
|||
{ |
|||
options.UseContentSecurityPolicyHeader = true; //false by default |
|||
options.ContentSecurityPolicyValue = "object-src 'none'; form-action 'self'; frame-ancestors 'none'"; //default value |
|||
|
|||
//adding additional security headers |
|||
options.Headers["Referrer-Policy"] = "no-referrer"; |
|||
}); |
|||
``` |
|||
|
|||
> If the header is the same, the additional security headers you defined take precedence over the default security headers. In other words, it overrides the default security headers' values. |
|||
|
|||
## Security Headers Middleware |
|||
|
|||
Security Headers middleware is an ASP.NET Core request pipeline [middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware) that adds pre-defined security headers to your application, including `X-Content-Type-Options`, `X-XSS-Protection`, and `X-Frame-Options`. Additionally, this middleware also includes those unique security headers in your application if you configure the `AbpSecurityHeadersOptions` as mentioned above. |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
app.UseAbpSecurityHeaders(); |
|||
``` |
|||
|
|||
> You can add this middleware into the `OnApplicationInitialization` method of your module class to register it to the request pipeline. This middleware is already configured in the [ABP Commercial Startup Templates](https://docs.abp.io/en/commercial/latest/startup-templates/index), so you don't need to manually add it if you are using one of these startup templates. |
|||
|
|||
After that, you have registered the `UseAbpSecurityHeaders` middleware into the request pipeline, the defined security headers will be shown in the response headers as in the figure below: |
|||
|
|||
 |
|||
|
|||
## Content Security Policy Script Nonce |
|||
|
|||
Abp Framework provides a property to add a dynamic script-src nonce value to the Content-Security-Policy header. With this feature, it automatically adds a dynamic nonce value to the header side. And with the help of the script tag helper, it adds this [`script nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) value to the script tags on your pages(The `ScriptNonceTagHelper` in the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` namespace must be attached as a taghelper.). |
|||
> If you need to add the nonce script manually, you can use 'Html.GetScriptNonce()' to add the nonce value or 'Html.GetScriptNonceAttribute()' to add the nonce attribute value. |
|||
|
|||
This feature is disabled by default. You can enable it by setting the `UseContentSecurityPolicyScriptNonce` property of the `AbpSecurityHeadersOptions` class to `true`. |
|||
|
|||
### Ignore Script Nonce |
|||
|
|||
You can ignore the script nonce for some pages or some selectors. You can use the `IgnoredScriptNoncePaths` and `IgnoredScriptNonceSelectors` properties of the `AbpSecurityHeadersOptions` class. |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
Configure<AbpSecurityHeadersOptions>(options => |
|||
{ |
|||
//adding script-src nonce |
|||
options.UseContentSecurityPolicyScriptNonce = true; //false by default |
|||
|
|||
//ignore script nonce source for these paths |
|||
options.IgnoredScriptNoncePaths.Add("/my-page"); |
|||
|
|||
//ignore script nonce by Elsa Workflows and other selectors |
|||
options.IgnoredScriptNonceSelectors.Add(context => |
|||
{ |
|||
var endpoint = context.GetEndpoint(); |
|||
return Task.FromResult(endpoint?.Metadata.GetMetadata<PageRouteMetadata>()?.RouteTemplate == "/{YOURHOSTPAGE}"); |
|||
}); |
|||
}); |
|||
``` |
|||
|
|||
### Ignore Abp Security Headers |
|||
|
|||
You can ignore the Abp Security Headers for some actions or pages. You can use the `IgnoreAbpSecurityHeaderAttribute` attribute for this. |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
@using Volo.Abp.AspNetCore.Security |
|||
@attribute [IgnoreAbpSecurityHeaderAttribute] |
|||
``` |
|||
|
|||
**Example:** |
|||
|
|||
```csharp |
|||
[IgnoreAbpSecurityHeaderAttribute] |
|||
public class IndexModel : AbpPageModel |
|||
{ |
|||
public void OnGet() |
|||
{ |
|||
} |
|||
} |
|||
``` |
|||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 12 KiB |