@ -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 |
|
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!**
|
||||
|
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,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 |