mirror of https://github.com/abpframework/abp.git
169 changed files with 2899 additions and 1587 deletions
@ -0,0 +1,29 @@ |
|||
name: Documentation Checks |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
- dev |
|||
paths: |
|||
# This ensures the check will only be run when something changes in the docs content |
|||
- "docs/en/**/*" |
|||
pull_request: |
|||
branches: |
|||
- dev |
|||
paths: |
|||
- "docs/en/**/*" |
|||
jobs: |
|||
spellcheck: |
|||
name: "Docs: Spellcheck (En)" |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/checkout@v2 |
|||
name: Check out the code |
|||
- uses: actions/setup-node@v1 |
|||
name: Setup node |
|||
with: |
|||
node-version: "16" |
|||
- run: npm install -g cspell |
|||
name: Install cSpell |
|||
- run: cspell --config ./cSpell.json "docs/en/**/*.md" --no-progress # Update for path to the markdown files |
|||
name: Run cSpell |
|||
@ -0,0 +1,151 @@ |
|||
{ |
|||
"version": "0.2", |
|||
"language": "en", |
|||
"words": [ |
|||
"ABP's", |
|||
"abpframework", |
|||
"Antiforgery", |
|||
"appsettings", |
|||
"aspnet", |
|||
"aspnetcore", |
|||
"Autofac", |
|||
"automagically", |
|||
"Blazor", |
|||
"CQRS", |
|||
"crossfade", |
|||
"Dapr", |
|||
"Datagrid's", |
|||
"Datatable", |
|||
"datepicker", |
|||
"dismissable", |
|||
"dockerized", |
|||
"entrypoints", |
|||
"findability", |
|||
"hoverable", |
|||
"Iddict", |
|||
"IntelliCode", |
|||
"Keysize", |
|||
"Linq", |
|||
"Microservices", |
|||
"middlewares", |
|||
"Minifier", |
|||
"multitenancy", |
|||
"multitenant", |
|||
"Navs", |
|||
"Newtonsoft", |
|||
"Npgsql", |
|||
"oidc", |
|||
"overridable", |
|||
"Parameterless", |
|||
"Passwordless", |
|||
"PKCE", |
|||
"preconfigured", |
|||
"proxying", |
|||
"redirections", |
|||
"scrollbars", |
|||
"signin", |
|||
"Templating", |
|||
"textboxes", |
|||
"toolset", |
|||
"unsubscription", |
|||
"Xunit" |
|||
], |
|||
"ignoreWords": [ |
|||
"Aliyun", |
|||
"Allibone", |
|||
"Blazorise", |
|||
"Boutwell", |
|||
"Cmskit", |
|||
"connectionstrings", |
|||
"Devart", |
|||
"Formik", |
|||
"Halil", |
|||
"Hanselman", |
|||
"hikalkan", |
|||
"Ibrahim", |
|||
"İbrahim", |
|||
"Kalkan", |
|||
"Kirti", |
|||
"Kommunity", |
|||
"Kulkarni", |
|||
"Luxon", |
|||
"malihu", |
|||
"Malik", |
|||
"Masis", |
|||
"Minio", |
|||
"NGXS", |
|||
"NSWAG", |
|||
"Scriban", |
|||
"Serilog", |
|||
"Shoudly", |
|||
"Shouldly", |
|||
"Sweetalert", |
|||
"Syncfusion", |
|||
"Telerik", |
|||
"Timeago", |
|||
"Toastr", |
|||
"Volo", |
|||
"Volosoft", |
|||
"Xeevis" |
|||
], |
|||
"patterns": [ |
|||
{ |
|||
"name": "Markdown links", |
|||
"pattern": "\\((.*)\\)", |
|||
"description": "" |
|||
}, |
|||
{ |
|||
"name": "Markdown code blocks", |
|||
"pattern": "/^(\\s*`{3,}).*[\\s\\S]*?^\\1/gmx", |
|||
"description": "Taken from the cSpell example at https://cspell.org/configuration/patterns/#verbose-regular-expressions" |
|||
}, |
|||
{ |
|||
"name": "Inline code blocks", |
|||
"pattern": "\\`([^\\`\\r\\n]+?)\\`", |
|||
"description": "https://stackoverflow.com/questions/41274241/how-to-capture-inline-markdown-code-but-not-a-markdown-code-fence-with-regex" |
|||
}, |
|||
{ |
|||
"name": "Link contents", |
|||
"pattern": "\\<a(.*)\\>", |
|||
"description": "" |
|||
}, |
|||
{ |
|||
"name": "Snippet references", |
|||
"pattern": "-- snippet:(.*)", |
|||
"description": "" |
|||
}, |
|||
{ |
|||
"name": "Snippet references 2", |
|||
"pattern": "\\<\\[sample:(.*)", |
|||
"description": "another kind of snippet reference" |
|||
}, |
|||
{ |
|||
"name": "Multi-line code blocks", |
|||
"pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm" |
|||
}, |
|||
{ |
|||
"name": "HTML Tags", |
|||
"pattern": "<[^>]*>", |
|||
"description": "Reference: https://stackoverflow.com/questions/11229831/regular-expression-to-remove-html-tags-from-a-string" |
|||
}, |
|||
{ |
|||
"name": "Markdown Image", |
|||
"pattern": "!\\[(.*)\\]\\((.*)\\)" |
|||
} |
|||
], |
|||
"ignoreRegExpList": [ |
|||
"Markdown links", |
|||
"Markdown code blocks", |
|||
"Inline code blocks", |
|||
"Link contents", |
|||
"Snippet references", |
|||
"Snippet references 2", |
|||
"Multi-line code blocks", |
|||
"HTML Tags", |
|||
"Markdown Image" |
|||
], |
|||
"ignorePaths": [ |
|||
"**/*Release/Post.md", |
|||
"**/*Preview/POST.md" |
|||
] |
|||
} |
|||
@ -0,0 +1,265 @@ |
|||
# Upgrade Your Existing Projects to .NET7 |
|||
|
|||
A new .NET version has come. As open-source contributors, we are tracking the latest libraries and adopting them to our existing projects. In this sense, we completed our .NET 7 upgrade in our repositories for ABP Framework and ABP Commercial. In this article, I'll share the experiences we faced while upgrading to the new .NET version 👉 .NET 7. |
|||
|
|||
When I wrote this article, the latest .NET version was `7.0.0-rc.2`. So some of the version statements I wrote below must be changed due to the stable version release. |
|||
|
|||
|
|||
|
|||
**To see the latest and greatest stuff, let's see how to upgrade our existing projects to .NET 7!** |
|||
|
|||
|
|||
|
|||
## Install .NET7 SDK |
|||
|
|||
If you are on your development computer, then you need to install the .NET7 SDK `7.x.x`. For the production servers, you need to install the .NET 7 runtimes. Download link for the .NET7 SDK and runtimes is: |
|||
|
|||
* https://dotnet.microsoft.com/en-us/download/dotnet/7.0 |
|||
|
|||
|
|||
|
|||
## Update Your *.csproj Files |
|||
|
|||
First, you need to update all your `*.csproj` files to support .NET7. Find and replace all your `<TargetFramework>*</TargetFramework>` in the `*.csproj` files to support .NET 7: |
|||
|
|||
```xml |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
``` |
|||
|
|||
We already did this in ABP Framework, see this commit as an example [github.com/abpframework/abp/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.csproj](https://github.com/abpframework/abp/blob/dev/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj#L4). |
|||
|
|||
|
|||
|
|||
### Microsoft Package Updates |
|||
|
|||
You must be using Microsoft packages as well; then you need to update them to the latest .NET 7 version. |
|||
At the time, I wrote this article, the latest version was `7.0.0-rc.2.22476.2`, so I'll update them to this version including minor version changes. |
|||
|
|||
Here's the list of all package reference updates I did: |
|||
|
|||
```xml |
|||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.0-rc.2.*" /> |
|||
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0-rc.2.*"> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0-rc.2.*"> |
|||
|
|||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0-rc.2.*" /> |
|||
|
|||
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="System.Text.Encodings.Web" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="System.Text.Json" Version="7.0.0-rc.2.*" /> |
|||
|
|||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="System.Collections.Immutable" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0-rc.2.*" /> |
|||
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.0-rc.2.*" /> |
|||
``` |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Entity Framework Core Updates |
|||
|
|||
If you use EF Core as your data access library, you should update your `dotnet-ef` CLI tool. Here's the terminal command to update it: |
|||
|
|||
```bash |
|||
dotnet tool update dotnet-ef --global --prerelease |
|||
``` |
|||
|
|||
We already did the the EF Core package reference update in the *Microsoft Package Updates* section. |
|||
|
|||
|
|||
|
|||
### Breaking Change: OrderBy |
|||
|
|||
This release makes a breaking change in an EF Core query running behavior. We faced this issue in some of our queries that were missing `OrderBy` statement. It throws an exception and does not run the query. Here's the explanation of a EF Core team member for this issue: [github.com/dotnet/efcore/issues/21202#issuecomment-913206415](https://github.com/dotnet/efcore/issues/21202#issuecomment-913206415). |
|||
|
|||
The following exception is being thrown: |
|||
|
|||
> InvalidOperationException: The query uses 'Skip' without specifying ordering and uses split query mode. This generates incorrect results. Either provide ordering or run query in single query mode using AsSingleQuery(). See https://go.microsoft.com/fwlink/?linkid=2196526 for more information |
|||
|
|||
If you don't want to add `OrderBy` statement to solve the issue, you can also use `AsSingleQuery()`. |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
### EF Core - SQL Client Connection String Update |
|||
|
|||
With this version, the behavior of the SQL connection has been changed. There is a keyword in the SQL connection string called `TrustServerCertificate`. This keyword indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust. |
|||
|
|||
> When `TrustServerCertificate` is set to `True`, the transport layer will use SSL to encrypt the channel and bypass walking the certificate chain to validate trust. If `TrustServerCertificate` is set to `true` and encryption is turned on, the encryption level specified on the server will be used even if `Encrypt` is set to `false`. The connection will fail otherwise. |
|||
|
|||
|
|||
|
|||
After the .NET7 update, it just started to throw the following exception: |
|||
|
|||
> A connection was successfully established with the server, but then an error occurred during the login process. |
|||
|
|||
|
|||
|
|||
We fixed this problem by adding the `TrustServerCertificate=true` to your connection string. Here's an example connection string that supports, |
|||
|
|||
````sql |
|||
Server=localhost; Database=MyProjectName; Trusted_Connection=True; TrustServerCertificate=True |
|||
```` |
|||
|
|||
See our commit for this fix: |
|||
|
|||
* [github.com/abpframework/abp/commit/96f17e67918eb87edd2baf876d4a7598281fe608](https://github.com/abpframework/abp/commit/96f17e67918eb87edd2baf876d4a7598281fe608) |
|||
|
|||
Related docs: |
|||
|
|||
* [learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes#encrypt-true](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes#encrypt-true) |
|||
* [stackoverflow.com/questions/34430550/a-connection-was-successfully-established-with-the-server-but-then-an-error-occ](https://stackoverflow.com/questions/34430550/a-connection-was-successfully-established-with-the-server-but-then-an-error-occ) |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Blazor Update |
|||
|
|||
Ensure that you have updated your Blazor project's csproj to support .NET7: |
|||
|
|||
```xml |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
``` |
|||
|
|||
### Install Blazor Workloads |
|||
|
|||
#### .NET Web Assembly build tools |
|||
|
|||
If you have a Blazor-WASM project, install the workloads by running the following in a command shell: |
|||
|
|||
```bash |
|||
dotnet workload install wasm-tools |
|||
``` |
|||
|
|||
**OR** you can update your workloads by running the following command in your Blazor project directory: |
|||
|
|||
```bash |
|||
dotnet workload restore |
|||
``` |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## .NET MAUI Update |
|||
|
|||
Ensure that you have updated your Blazor project's csproj to support .NET7: |
|||
|
|||
```xml |
|||
<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> |
|||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks> |
|||
``` |
|||
|
|||
|
|||
|
|||
### Install MAUI Workloads |
|||
|
|||
If you have .NET MAUI project, then you also need to update your `TargetFramework` as below: |
|||
|
|||
If you have a .NET MAUI project, after installing the .NET 7 SDK, install the latest workloads with the following command: |
|||
|
|||
```bash |
|||
dotnet workload install maui |
|||
``` |
|||
|
|||
**OR** run the following command in your existing .NET MAUI project directory |
|||
|
|||
```bash |
|||
dotnet workload restore |
|||
``` |
|||
|
|||
|
|||
|
|||
Further information, check out https://github.com/dotnet/maui/wiki/.NET-7-and-.NET-MAUI |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
#### Dotnet Maui Check Tool |
|||
|
|||
Alternatively, there's a 3rd party tool for .NET MAUI to install the required workloads. This tool installs the missing SDK packs. You can reach the tool's repository at [github.com/Redth/dotnet-maui-check](https://github.com/Redth/dotnet-maui-check). |
|||
|
|||
Installation: |
|||
|
|||
```bash |
|||
dotnet tool install -g Redth.Net.Maui.Check |
|||
``` |
|||
|
|||
Run: |
|||
|
|||
```bash |
|||
maui-check |
|||
``` |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Docker Image Update |
|||
|
|||
If you are using Docker to automate the deployment of applications, you also need to update your images. We were using `aspnet:6.0.0-bullseye-slim` base and after the .NET 7 update, we started using `aspnet:7.0-bullseye-slim` in our Docker files. |
|||
|
|||
``` |
|||
FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim AS base |
|||
``` |
|||
|
|||
For this update, you can check out the following commit as an example: |
|||
|
|||
* [github.com/abpframework/abp/commit/2d07b9bd00152bef4658c48ff9b2cbee5788d308](https://github.com/abpframework/abp/commit/2d07b9bd00152bef4658c48ff9b2cbee5788d308) |
|||
|
|||
|
|||
|
|||
## ABP Framework .NET 7 Update |
|||
|
|||
In [ABP Framework repository](https://github.com/abpframework/abp), we pdated all our dependencies from .NET 6 to .NET 7. |
|||
Not all the changes are here, but you can check out the following PR of the .NET 7 update: |
|||
|
|||
* [github.com/abpframework/abp/pull/13626/files](https://github.com/abpframework/abp/pull/13626/files) |
|||
|
|||
|
|||
... |
|||
|
|||
Happy coding with .NET 7 🤗 |
|||
|
|||
... |
|||
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 133 KiB |
@ -0,0 +1,36 @@ |
|||
# ABP Version 7.0 Migration Guide |
|||
|
|||
This document is a guide for upgrading ABP v6.0 solutions to ABP v7.0. There is a change in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. |
|||
|
|||
## Hybrid JSON was removed. |
|||
|
|||
Since [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview) library supports more custom features in NET 7, ABP no longer need the hybrid Json feature. |
|||
|
|||
### Previous Behavior |
|||
|
|||
There is a `Volo.Abp.Json` package which contains the `AbpJsonModule` module. |
|||
`Serialization/deserialization` features of [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview) and [Nettonsoft](https://www.newtonsoft.com/json/help/html/SerializingJSON.htm) are implemented in this module. |
|||
|
|||
We use [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview) first, More custom cases can be handled with [Nettonsoft](https://www.newtonsoft.com/json/help/html/SerializingJSON.htm) by configuring `UnsupportedTypes` of `AbpSystemTextJsonSerializerOptions`. |
|||
|
|||
### New Behavior |
|||
|
|||
We created `Volo.Abp.Json.SystemTextJson` and `Volo.Abp.Json.Newtonsoft` as separate packages, which means you can only use one of them in your project. The default is to use `SystemTextJson`. If you want `Newtonsoft`, please also use `Volo.Abp.AspNetCore.Mvc.NewtonsoftJson` in your web project. |
|||
|
|||
* Volo.Abp.Json.Abstractions |
|||
* Volo.Abp.Json.Newtonsoft |
|||
* Volo.Abp.Json.SystemTextJson |
|||
* Volo.Abp.Json (Depends on `Volo.Abp.Json.SystemTextJson` by default to prevent breaking) |
|||
* Volo.Abp.AspNetCore.Mvc.NewtonsoftJson |
|||
|
|||
The `AbpJsonOptions` now has only two properties, which are |
|||
|
|||
* `InputDateTimeFormats(List<string>)`: Formats of input JSON date, Empty string means default format. You can provide multiple formats to parse the date. |
|||
* `OutputDateTimeFormat(string)`: Format of output json date, Null or empty string means default format. |
|||
|
|||
Please remove all `UnsupportedTypes` add custom `Modifiers` to control serialization/deserialization behavior. |
|||
|
|||
Check the docs to see the more info: https://github.com/abpframework/abp/blob/dev/docs/en/JSON.md#configuration |
|||
|
|||
Check the docs to see how to customize a JSON contract: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/custom-contracts |
|||
|
|||
|
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,29 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc> |
|||
<AssemblyName>Volo.Abp.AspNetCore.Mvc.NewtonsoftJson</AssemblyName> |
|||
<PackageId>Volo.Abp.AspNetCore.Mvc.NewtonsoftJson</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<IsPackable>true</IsPackable> |
|||
<OutputType>Library</OutputType> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Newtonsoft\Volo.Abp.Json.Newtonsoft.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(MicrosoftAspNetCorePackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.Newtonsoft; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.NewtonsoftJson; |
|||
|
|||
[DependsOn(typeof(AbpJsonNewtonsoftModule), typeof(AbpAspNetCoreMvcModule))] |
|||
public class AbpAspNetCoreMvcNewtonsoftModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddMvcCore().AddNewtonsoftJson(); |
|||
|
|||
context.Services.AddOptions<MvcNewtonsoftJsonOptions>() |
|||
.Configure<AbpCamelCasePropertyNamesContractResolver>((options, contractResolver) => |
|||
{ |
|||
options.SerializerSettings.ContractResolver = contractResolver; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc.Formatters; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpHybridJsonInputFormatter : TextInputFormatter, IInputFormatterExceptionPolicy |
|||
{ |
|||
private readonly SystemTextJsonInputFormatter _systemTextJsonInputFormatter; |
|||
private readonly NewtonsoftJsonInputFormatter _newtonsoftJsonInputFormatter; |
|||
|
|||
public AbpHybridJsonInputFormatter(SystemTextJsonInputFormatter systemTextJsonInputFormatter, NewtonsoftJsonInputFormatter newtonsoftJsonInputFormatter) |
|||
{ |
|||
_systemTextJsonInputFormatter = systemTextJsonInputFormatter; |
|||
_newtonsoftJsonInputFormatter = newtonsoftJsonInputFormatter; |
|||
|
|||
SupportedEncodings.Add(UTF8EncodingWithoutBOM); |
|||
SupportedEncodings.Add(UTF16EncodingLittleEndian); |
|||
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); |
|||
} |
|||
|
|||
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) |
|||
{ |
|||
return await GetTextInputFormatter(context).ReadRequestBodyAsync(context, encoding); |
|||
} |
|||
|
|||
protected virtual TextInputFormatter GetTextInputFormatter(InputFormatterContext context) |
|||
{ |
|||
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonUnsupportedTypeMatcher>(); |
|||
|
|||
if (!typesMatcher.Match(context.ModelType)) |
|||
{ |
|||
return _systemTextJsonInputFormatter; |
|||
} |
|||
|
|||
return _newtonsoftJsonInputFormatter; |
|||
} |
|||
|
|||
public virtual InputFormatterExceptionPolicy ExceptionPolicy => InputFormatterExceptionPolicy.MalformedInputExceptions; |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
using System.Buffers; |
|||
using System.Text.Encodings.Web; |
|||
using System.Text.Json; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Formatters; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpHybridJsonOptionsSetup : IConfigureOptions<MvcOptions> |
|||
{ |
|||
private readonly IOptions<JsonOptions> _jsonOptions; |
|||
private readonly IOptions<MvcNewtonsoftJsonOptions> _mvcNewtonsoftJsonOptions; |
|||
private readonly ILoggerFactory _loggerFactory; |
|||
private readonly ArrayPool<char> _charPool; |
|||
private readonly ObjectPoolProvider _objectPoolProvider; |
|||
|
|||
public AbpHybridJsonOptionsSetup( |
|||
IOptions<JsonOptions> jsonOptions, |
|||
IOptions<MvcNewtonsoftJsonOptions> mvcNewtonsoftJsonOptions, |
|||
ILoggerFactory loggerFactory, |
|||
ArrayPool<char> charPool, |
|||
ObjectPoolProvider objectPoolProvider) |
|||
{ |
|||
_jsonOptions = jsonOptions; |
|||
_mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions; |
|||
_loggerFactory = loggerFactory; |
|||
_charPool = charPool; |
|||
_objectPoolProvider = objectPoolProvider; |
|||
} |
|||
|
|||
public void Configure(MvcOptions options) |
|||
{ |
|||
var systemTextJsonInputFormatter = new SystemTextJsonInputFormatter( |
|||
_jsonOptions.Value, |
|||
_loggerFactory.CreateLogger<SystemTextJsonInputFormatter>()); |
|||
|
|||
var newtonsoftJsonInputFormatter = new NewtonsoftJsonInputFormatter( |
|||
_loggerFactory.CreateLogger<NewtonsoftJsonInputFormatter>(), |
|||
_mvcNewtonsoftJsonOptions.Value.SerializerSettings, |
|||
_charPool, |
|||
_objectPoolProvider, |
|||
options, |
|||
_mvcNewtonsoftJsonOptions.Value); |
|||
|
|||
options.InputFormatters.RemoveType<SystemTextJsonInputFormatter>(); |
|||
options.InputFormatters.RemoveType<NewtonsoftJsonInputFormatter>(); |
|||
options.InputFormatters.Add(new AbpHybridJsonInputFormatter(systemTextJsonInputFormatter, newtonsoftJsonInputFormatter)); |
|||
|
|||
var jsonSerializerOptions = _jsonOptions.Value.JsonSerializerOptions; |
|||
if (jsonSerializerOptions.Encoder is null) |
|||
{ |
|||
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
|
|||
jsonSerializerOptions = new JsonSerializerOptions(jsonSerializerOptions) |
|||
{ |
|||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, |
|||
}; |
|||
} |
|||
|
|||
var systemTextJsonOutputFormatter = new SystemTextJsonOutputFormatter(jsonSerializerOptions); |
|||
var newtonsoftJsonOutputFormatter = new NewtonsoftJsonOutputFormatter( |
|||
_mvcNewtonsoftJsonOptions.Value.SerializerSettings, |
|||
_charPool, |
|||
options); |
|||
|
|||
options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>(); |
|||
options.OutputFormatters.RemoveType<NewtonsoftJsonOutputFormatter>(); |
|||
options.OutputFormatters.Add(new AbpHybridJsonOutputFormatter(systemTextJsonOutputFormatter, newtonsoftJsonOutputFormatter)); |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc.Formatters; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpHybridJsonOutputFormatter : TextOutputFormatter |
|||
{ |
|||
private readonly SystemTextJsonOutputFormatter _systemTextJsonOutputFormatter; |
|||
private readonly NewtonsoftJsonOutputFormatter _newtonsoftJsonOutputFormatter; |
|||
|
|||
public AbpHybridJsonOutputFormatter(SystemTextJsonOutputFormatter systemTextJsonOutputFormatter, NewtonsoftJsonOutputFormatter newtonsoftJsonOutputFormatter) |
|||
{ |
|||
_systemTextJsonOutputFormatter = systemTextJsonOutputFormatter; |
|||
_newtonsoftJsonOutputFormatter = newtonsoftJsonOutputFormatter; |
|||
|
|||
SupportedEncodings.Add(Encoding.UTF8); |
|||
SupportedEncodings.Add(Encoding.Unicode); |
|||
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson); |
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax); |
|||
} |
|||
|
|||
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) |
|||
{ |
|||
await GetTextInputFormatter(context).WriteResponseBodyAsync(context, selectedEncoding); |
|||
} |
|||
|
|||
protected virtual TextOutputFormatter GetTextInputFormatter(OutputFormatterWriteContext context) |
|||
{ |
|||
var typesMatcher = context.HttpContext.RequestServices.GetRequiredService<AbpSystemTextJsonUnsupportedTypeMatcher>(); |
|||
if (!typesMatcher.Match(context.ObjectType)) |
|||
{ |
|||
return _systemTextJsonOutputFormatter; |
|||
} |
|||
|
|||
return _newtonsoftJsonOutputFormatter; |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
using System; |
|||
using System.Text.Json; |
|||
using System.Text.Json.Serialization; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpJsonOptionsSetup : IConfigureOptions<JsonOptions> |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
public AbpJsonOptionsSetup(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public void Configure(JsonOptions options) |
|||
{ |
|||
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; |
|||
options.JsonSerializerOptions.AllowTrailingCommas = true; |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpDateTimeConverter>()); |
|||
options.JsonSerializerOptions.Converters.Add(ServiceProvider.GetRequiredService<AbpNullableDateTimeConverter>()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
} |
|||
} |
|||
@ -1,48 +0,0 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json.Newtonsoft; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpMvcJsonContractResolver : DefaultContractResolver, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpMvcJsonContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>, |
|||
true |
|||
); |
|||
|
|||
NamingStrategy = new CamelCaseNamingStrategy(); |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
ModifyProperty(member, property); |
|||
|
|||
return property; |
|||
} |
|||
|
|||
protected virtual void ModifyProperty(MemberInfo member, JsonProperty property) |
|||
{ |
|||
if (property.PropertyType != typeof(DateTime) && property.PropertyType != typeof(DateTime?)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public class AbpMvcNewtonsoftJsonOptionsSetup : IConfigureOptions<MvcNewtonsoftJsonOptions> |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
public AbpMvcNewtonsoftJsonOptionsSetup(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public void Configure(MvcNewtonsoftJsonOptions options) |
|||
{ |
|||
options.SerializerSettings.ContractResolver = ServiceProvider.GetRequiredService<AbpMvcJsonContractResolver>(); |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using Microsoft.Net.Http.Headers; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
/// <summary>
|
|||
/// https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.NewtonsoftJson/src/MediaTypeHeaderValues.cs
|
|||
/// </summary>
|
|||
internal static class MediaTypeHeaderValues |
|||
{ |
|||
public static readonly MediaTypeHeaderValue ApplicationJson = MediaTypeHeaderValue.Parse("application/json").CopyAsReadOnly(); |
|||
|
|||
public static readonly MediaTypeHeaderValue TextJson = MediaTypeHeaderValue.Parse("text/json").CopyAsReadOnly(); |
|||
|
|||
public static readonly MediaTypeHeaderValue ApplicationAnyJsonSyntax = MediaTypeHeaderValue.Parse("application/*+json").CopyAsReadOnly(); |
|||
|
|||
public static readonly MediaTypeHeaderValue ApplicationXml = MediaTypeHeaderValue.Parse("application/xml").CopyAsReadOnly(); |
|||
|
|||
public static readonly MediaTypeHeaderValue TextXml = MediaTypeHeaderValue.Parse("text/xml").CopyAsReadOnly(); |
|||
|
|||
public static readonly MediaTypeHeaderValue ApplicationAnyXmlSyntax = MediaTypeHeaderValue.Parse("application/*+xml").CopyAsReadOnly(); |
|||
} |
|||
@ -1,28 +1,31 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System; |
|||
using System.Text.Json; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
|||
|
|||
public static class MvcCoreBuilderExtensions |
|||
{ |
|||
public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder) |
|||
public static IMvcCoreBuilder AddAbpJson(this IMvcCoreBuilder builder) |
|||
{ |
|||
var abpJsonOptions = builder.Services.ExecutePreConfiguredActions<AbpJsonOptions>(); |
|||
if (!abpJsonOptions.UseHybridSerializer) |
|||
{ |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
|||
builder.AddNewtonsoftJson(); |
|||
return builder; |
|||
} |
|||
builder.Services.AddOptions<JsonOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; |
|||
options.JsonSerializerOptions.AllowTrailingCommas = true; |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
|
|||
options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(serviceProvider |
|||
.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>()); |
|||
}); |
|||
|
|||
builder.Services.TryAddTransient<DefaultObjectPoolProvider>(); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<JsonOptions>, AbpJsonOptionsSetup>()); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
|||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, AbpHybridJsonOptionsSetup>()); |
|||
return builder; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Volo.Abp.AspNetCore.TestBase; |
|||
|
|||
public class AbpNoopHostLifetime : IHostLifetime |
|||
{ |
|||
public Task StopAsync(CancellationToken cancellationToken) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task WaitForStartAsync(CancellationToken cancellationToken) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Hosting.Server; |
|||
using Microsoft.AspNetCore.TestHost; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Volo.Abp.AspNetCore.TestBase; |
|||
|
|||
public static class AbpWebHostBuilderExtensions |
|||
{ |
|||
public static IWebHostBuilder UseAbpTestServer(this IWebHostBuilder builder) |
|||
{ |
|||
return builder.ConfigureServices(services => |
|||
{ |
|||
services.AddScoped<IHostLifetime, AbpNoopHostLifetime>(); |
|||
services.AddScoped<IServer, TestServer>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
public class AuditingContractResolver : CamelCasePropertyNamesContractResolver |
|||
{ |
|||
private readonly List<Type> _ignoredTypes; |
|||
|
|||
public AuditingContractResolver(List<Type> ignoredTypes) |
|||
{ |
|||
_ignoredTypes = ignoredTypes; |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (_ignoredTypes.Any(ignoredType => ignoredType.GetTypeInfo().IsAssignableFrom(property.PropertyType))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
return property; |
|||
} |
|||
|
|||
if (member.DeclaringType != null && (member.DeclaringType.IsDefined(typeof(DisableAuditingAttribute)) || member.DeclaringType.IsDefined(typeof(JsonIgnoreAttribute)))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
return property; |
|||
} |
|||
|
|||
if (member.IsDefined(typeof(DisableAuditingAttribute)) || member.IsDefined(typeof(JsonIgnoreAttribute))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
using System.Collections.Concurrent; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Text.Json; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
public class JsonAuditSerializer : IAuditSerializer, ITransientDependency |
|||
{ |
|||
protected AbpAuditingOptions Options; |
|||
|
|||
public JsonAuditSerializer(IOptions<AbpAuditingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public string Serialize(object obj) |
|||
{ |
|||
return JsonSerializer.Serialize(obj, CreateJsonSerializerOptions()); |
|||
} |
|||
|
|||
private static readonly ConcurrentDictionary<string, JsonSerializerOptions> JsonSerializerOptionsCache = |
|||
new ConcurrentDictionary<string, JsonSerializerOptions>(); |
|||
|
|||
protected virtual JsonSerializerOptions CreateJsonSerializerOptions() |
|||
{ |
|||
return JsonSerializerOptionsCache.GetOrAdd(nameof(JsonAuditSerializer), _ => |
|||
{ |
|||
var settings = new JsonSerializerOptions() |
|||
{ |
|||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, |
|||
TypeInfoResolver = new DefaultJsonTypeInfoResolver() |
|||
{ |
|||
Modifiers = |
|||
{ |
|||
jsonTypeInfo => |
|||
{ |
|||
if (Options.IgnoredTypes.Any(ignoredType => ignoredType.IsAssignableFrom(jsonTypeInfo.Type)) || |
|||
jsonTypeInfo.Type.GetCustomAttributes(typeof(DisableAuditingAttribute), false).Any()) |
|||
{ |
|||
if (jsonTypeInfo.Kind == JsonTypeInfoKind.Object) |
|||
{ |
|||
jsonTypeInfo.Properties.Clear(); |
|||
} |
|||
} |
|||
|
|||
foreach (var property in jsonTypeInfo.Properties) |
|||
{ |
|||
if (Options.IgnoredTypes.Any(ignoredType => ignoredType.IsAssignableFrom(property.PropertyType))) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
|
|||
if (property.AttributeProvider != null && |
|||
property.AttributeProvider.GetCustomAttributes(typeof(DisableAuditingAttribute), false).Any()) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
|
|||
if (property.PropertyType.DeclaringType != null && |
|||
property.PropertyType.DeclaringType.IsDefined(typeof(DisableAuditingAttribute))) |
|||
{ |
|||
property.ShouldSerialize = (_, _) => false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
return settings; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,43 +0,0 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
//TODO: Rename to JsonAuditSerializer
|
|||
public class JsonNetAuditSerializer : IAuditSerializer, ITransientDependency |
|||
{ |
|||
protected AbpAuditingOptions Options; |
|||
|
|||
public JsonNetAuditSerializer(IOptions<AbpAuditingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public string Serialize(object obj) |
|||
{ |
|||
return JsonConvert.SerializeObject(obj, GetSharedJsonSerializerSettings()); |
|||
} |
|||
|
|||
private static readonly object SyncObj = new object(); |
|||
private static JsonSerializerSettings _sharedJsonSerializerSettings; |
|||
|
|||
private JsonSerializerSettings GetSharedJsonSerializerSettings() |
|||
{ |
|||
if (_sharedJsonSerializerSettings == null) |
|||
{ |
|||
lock (SyncObj) |
|||
{ |
|||
if (_sharedJsonSerializerSettings == null) |
|||
{ |
|||
_sharedJsonSerializerSettings = new JsonSerializerSettings |
|||
{ |
|||
ContractResolver = new AuditingContractResolver(Options.IgnoredTypes) |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return _sharedJsonSerializerSettings; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<PackageId>Volo.Abp.Json.Abstractions</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpJsonAbstractionsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Json; |
|||
|
|||
public class AbpJsonOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Formats of input JSON date, Empty string means default format.
|
|||
/// </summary>
|
|||
public List<string> InputDateTimeFormats { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Format of output json date, Null or empty string means default format.
|
|||
/// </summary>
|
|||
public string OutputDateTimeFormat { get; set; } |
|||
|
|||
public AbpJsonOptions() |
|||
{ |
|||
InputDateTimeFormats = new List<string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Json.Newtonsoft</AssemblyName> |
|||
<PackageId>Volo.Abp.Json.Newtonsoft</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Abstractions\Volo.Abp.Json.Abstractions.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" /> |
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpCamelCasePropertyNamesContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpDateTimeConverter>, |
|||
true |
|||
); |
|||
|
|||
NamingStrategy = new CamelCaseNamingStrategy |
|||
{ |
|||
ProcessDictionaryKeys = false |
|||
}; |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (AbpDateTimeConverter.ShouldNormalize(member, property)) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Converters; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpDateTimeConverter : DateTimeConverterBase |
|||
{ |
|||
private readonly string _dateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; |
|||
private readonly DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind; |
|||
private readonly CultureInfo _culture = CultureInfo.InvariantCulture; |
|||
private readonly IClock _clock; |
|||
private readonly AbpJsonOptions _options; |
|||
|
|||
public AbpDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> options) |
|||
{ |
|||
_clock = clock; |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public override bool CanConvert(Type objectType) |
|||
{ |
|||
return objectType == typeof(DateTime) || objectType == typeof(DateTime?); |
|||
} |
|||
|
|||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) |
|||
{ |
|||
var nullable = Nullable.GetUnderlyingType(objectType) != null; |
|||
if (reader.TokenType == JsonToken.Null) |
|||
{ |
|||
if (!nullable) |
|||
{ |
|||
throw new JsonSerializationException($"Cannot convert null value to {objectType.FullName}."); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
if (reader.TokenType == JsonToken.Date) |
|||
{ |
|||
return _clock.Normalize(reader.Value.To<DateTime>()); |
|||
} |
|||
|
|||
if (reader.TokenType != JsonToken.String) |
|||
{ |
|||
throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}."); |
|||
} |
|||
|
|||
var dateText = reader.Value?.ToString(); |
|||
|
|||
if (dateText.IsNullOrEmpty() && nullable) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
if (_options.InputDateTimeFormats.Any()) |
|||
{ |
|||
foreach (var format in _options.InputDateTimeFormats) |
|||
{ |
|||
if (DateTime.TryParseExact(dateText, format, _culture, _dateTimeStyles, out var d1)) |
|||
{ |
|||
return _clock.Normalize(d1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var date = !_dateTimeFormat.IsNullOrEmpty() ? |
|||
DateTime.ParseExact(dateText, _dateTimeFormat, _culture, _dateTimeStyles) : |
|||
DateTime.Parse(dateText, _culture, _dateTimeStyles); |
|||
|
|||
return _clock.Normalize(date); |
|||
} |
|||
|
|||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) |
|||
{ |
|||
if (value != null) |
|||
{ |
|||
value = _clock.Normalize(value.To<DateTime>()); |
|||
} |
|||
|
|||
if (value is DateTime dateTime) |
|||
{ |
|||
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal || |
|||
(_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) |
|||
{ |
|||
dateTime = dateTime.ToUniversalTime(); |
|||
} |
|||
|
|||
writer.WriteValue(_options.OutputDateTimeFormat.IsNullOrWhiteSpace() |
|||
? dateTime.ToString(_dateTimeFormat, _culture) |
|||
: dateTime.ToString(_options.OutputDateTimeFormat, _culture)); |
|||
} |
|||
else |
|||
{ |
|||
throw new JsonSerializationException($"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}."); |
|||
} |
|||
} |
|||
|
|||
internal static bool ShouldNormalize(MemberInfo member, JsonProperty property) |
|||
{ |
|||
if (property.PropertyType != typeof(DateTime) && |
|||
property.PropertyType != typeof(DateTime?)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpDefaultContractResolver : DefaultContractResolver, ITransientDependency |
|||
{ |
|||
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter; |
|||
|
|||
public AbpDefaultContractResolver(IServiceProvider serviceProvider) |
|||
{ |
|||
_dateTimeConverter = new Lazy<AbpDateTimeConverter>( |
|||
serviceProvider.GetRequiredService<AbpDateTimeConverter>, |
|||
true |
|||
); |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
var property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (AbpDateTimeConverter.ShouldNormalize(member, property)) |
|||
{ |
|||
property.Converter = _dateTimeConverter.Value; |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
[DependsOn(typeof(AbpJsonAbstractionsModule), typeof(AbpTimingModule))] |
|||
public class AbpJsonNewtonsoftModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddOptions<AbpNewtonsoftJsonSerializerOptions>() |
|||
.Configure<AbpCamelCasePropertyNamesContractResolver>((options, contractResolver) => |
|||
{ |
|||
options.JsonSerializerSettings.ContractResolver = contractResolver; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,96 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class AbpNewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected IOptions<AbpNewtonsoftJsonSerializerOptions> Options { get; } |
|||
|
|||
public AbpNewtonsoftJsonSerializer(IServiceProvider serviceProvider, IOptions<AbpNewtonsoftJsonSerializerOptions> options) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options; |
|||
} |
|||
|
|||
public string Serialize(object obj, bool camelCase = true, bool indented = false) |
|||
{ |
|||
return JsonConvert.SerializeObject(obj, CreateJsonSerializerOptions(camelCase, indented)); |
|||
} |
|||
|
|||
public T Deserialize<T>(string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject<T>(jsonString, CreateJsonSerializerOptions(camelCase)); |
|||
} |
|||
|
|||
public object Deserialize(Type type, string jsonString, bool camelCase = true) |
|||
{ |
|||
return JsonConvert.DeserializeObject(jsonString, type, CreateJsonSerializerOptions(camelCase)); |
|||
} |
|||
|
|||
private static readonly ConcurrentDictionary<object, JsonSerializerSettings> JsonSerializerOptionsCache = |
|||
new ConcurrentDictionary<object, JsonSerializerSettings>(); |
|||
|
|||
protected virtual JsonSerializerSettings CreateJsonSerializerOptions(bool camelCase = true, bool indented = false) |
|||
{ |
|||
return JsonSerializerOptionsCache.GetOrAdd(new |
|||
{ |
|||
camelCase, |
|||
indented |
|||
}, _ => |
|||
{ |
|||
var settings = new JsonSerializerSettings |
|||
{ |
|||
Binder = Options.Value.JsonSerializerSettings.Binder, |
|||
CheckAdditionalContent = Options.Value.JsonSerializerSettings.CheckAdditionalContent, |
|||
Context = Options.Value.JsonSerializerSettings.Context, |
|||
ContractResolver = Options.Value.JsonSerializerSettings.ContractResolver, |
|||
ConstructorHandling = Options.Value.JsonSerializerSettings.ConstructorHandling, |
|||
Converters = Options.Value.JsonSerializerSettings.Converters, |
|||
Culture = Options.Value.JsonSerializerSettings.Culture, |
|||
DateFormatHandling = Options.Value.JsonSerializerSettings.DateFormatHandling, |
|||
DateFormatString = Options.Value.JsonSerializerSettings.DateFormatString, |
|||
DateParseHandling = Options.Value.JsonSerializerSettings.DateParseHandling, |
|||
DateTimeZoneHandling = Options.Value.JsonSerializerSettings.DateTimeZoneHandling, |
|||
DefaultValueHandling = Options.Value.JsonSerializerSettings.DefaultValueHandling, |
|||
Error = Options.Value.JsonSerializerSettings.Error, |
|||
EqualityComparer = Options.Value.JsonSerializerSettings.EqualityComparer, |
|||
FloatFormatHandling = Options.Value.JsonSerializerSettings.FloatFormatHandling, |
|||
FloatParseHandling = Options.Value.JsonSerializerSettings.FloatParseHandling, |
|||
Formatting = Options.Value.JsonSerializerSettings.Formatting, |
|||
MaxDepth = Options.Value.JsonSerializerSettings.MaxDepth, |
|||
MetadataPropertyHandling = Options.Value.JsonSerializerSettings.MetadataPropertyHandling, |
|||
MissingMemberHandling = Options.Value.JsonSerializerSettings.MissingMemberHandling, |
|||
NullValueHandling = Options.Value.JsonSerializerSettings.NullValueHandling, |
|||
ObjectCreationHandling = Options.Value.JsonSerializerSettings.ObjectCreationHandling, |
|||
PreserveReferencesHandling = Options.Value.JsonSerializerSettings.PreserveReferencesHandling, |
|||
ReferenceLoopHandling = Options.Value.JsonSerializerSettings.ReferenceLoopHandling, |
|||
ReferenceResolver = Options.Value.JsonSerializerSettings.ReferenceResolver, |
|||
ReferenceResolverProvider = Options.Value.JsonSerializerSettings.ReferenceResolverProvider, |
|||
SerializationBinder = Options.Value.JsonSerializerSettings.SerializationBinder, |
|||
StringEscapeHandling = Options.Value.JsonSerializerSettings.StringEscapeHandling, |
|||
TraceWriter = Options.Value.JsonSerializerSettings.TraceWriter, |
|||
TypeNameAssemblyFormat = Options.Value.JsonSerializerSettings.TypeNameAssemblyFormat, |
|||
TypeNameHandling = Options.Value.JsonSerializerSettings.TypeNameHandling, |
|||
TypeNameAssemblyFormatHandling = Options.Value.JsonSerializerSettings.TypeNameAssemblyFormatHandling |
|||
}; |
|||
|
|||
settings.ContractResolver = camelCase |
|||
? ServiceProvider.GetRequiredService<AbpCamelCasePropertyNamesContractResolver>() |
|||
: ServiceProvider.GetRequiredService<AbpDefaultContractResolver>(); |
|||
|
|||
if (indented) |
|||
{ |
|||
settings.Formatting = Formatting.Indented; |
|||
} |
|||
|
|||
return settings; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,14 +1,13 @@ |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace Volo.Abp.Json.Newtonsoft; |
|||
|
|||
public class AbpNewtonsoftJsonSerializerOptions |
|||
{ |
|||
public ITypeList<JsonConverter> Converters { get; } |
|||
public JsonSerializerSettings JsonSerializerSettings { get; } |
|||
|
|||
public AbpNewtonsoftJsonSerializerOptions() |
|||
{ |
|||
Converters = new TypeList<JsonConverter>(); |
|||
JsonSerializerSettings = new JsonSerializerSettings(); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Json.SystemTextJson</AssemblyName> |
|||
<PackageId>Volo.Abp.Json.SystemTextJson</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Json.Abstractions\Volo.Abp.Json.Abstractions.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" /> |
|||
<PackageReference Include="System.Text.Json" Version="$(MicrosoftAspNetCorePackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,16 @@ |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
public class AbpDefaultJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, ITransientDependency |
|||
{ |
|||
public AbpDefaultJsonTypeInfoResolver(IOptions<AbpSystemTextJsonSerializerModifiersOptions> options) |
|||
{ |
|||
foreach (var modifier in options.Value.Modifiers) |
|||
{ |
|||
Modifiers.Add(modifier); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System; |
|||
using System.Text.Encodings.Web; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
[DependsOn(typeof(AbpJsonAbstractionsModule), typeof(AbpTimingModule))] |
|||
public class AbpJsonSystemTextJsonModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddOptions<AbpSystemTextJsonSerializerOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
|
|||
options.JsonSerializerOptions.Encoder ??= JavaScriptEncoder.UnsafeRelaxedJsonEscaping; |
|||
|
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToEnumFactory()); |
|||
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter()); |
|||
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter()); |
|||
|
|||
options.JsonSerializerOptions.TypeInfoResolver = new AbpDefaultJsonTypeInfoResolver(serviceProvider |
|||
.GetRequiredService<IOptions<AbpSystemTextJsonSerializerModifiersOptions>>()); |
|||
}); |
|||
|
|||
context.Services.AddOptions<AbpSystemTextJsonSerializerModifiersOptions>() |
|||
.Configure<IServiceProvider>((options, serviceProvider) => |
|||
{ |
|||
options.Modifiers.Add(new AbpDateTimeConverterModifier().CreateModifyAction(serviceProvider)); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson; |
|||
|
|||
public class AbpSystemTextJsonSerializerModifiersOptions |
|||
{ |
|||
public List<Action<JsonTypeInfo>> Modifiers { get; } |
|||
|
|||
public AbpSystemTextJsonSerializerModifiersOptions() |
|||
{ |
|||
Modifiers = new List<Action<JsonTypeInfo>> |
|||
{ |
|||
AbpIncludeExtraPropertiesModifiers.Modify, |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public class AbpDateTimeConverterModifier |
|||
{ |
|||
private IServiceProvider _serviceProvider; |
|||
|
|||
public Action<JsonTypeInfo> CreateModifyAction(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
return Modify; |
|||
} |
|||
|
|||
private void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (ReflectionHelper.GetAttributesOfMemberOrDeclaringType<DisableDateTimeNormalizationAttribute>(jsonTypeInfo.Type).Any()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var property in jsonTypeInfo.Properties.Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?))) |
|||
{ |
|||
if (property.AttributeProvider == null || |
|||
!property.AttributeProvider.GetCustomAttributes(typeof(DisableDateTimeNormalizationAttribute), false).Any()) |
|||
{ |
|||
property.CustomConverter = property.PropertyType == typeof(DateTime) |
|||
? _serviceProvider.GetRequiredService<AbpDateTimeConverter>() |
|||
: _serviceProvider.GetRequiredService<AbpNullableDateTimeConverter>(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq.Expressions; |
|||
using System.Reflection; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public class AbpIgnorePropertiesModifiers<TClass, TProperty> |
|||
where TClass : class |
|||
{ |
|||
private Expression<Func<TClass, TProperty>> _propertySelector; |
|||
|
|||
public Action<JsonTypeInfo> CreateModifyAction(Expression<Func<TClass, TProperty>> propertySelector) |
|||
{ |
|||
_propertySelector = propertySelector; |
|||
return Modify; |
|||
} |
|||
|
|||
public void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (jsonTypeInfo.Type == typeof(TClass)) |
|||
{ |
|||
jsonTypeInfo.Properties.RemoveAll( |
|||
x => x.AttributeProvider is MemberInfo memberInfo && |
|||
memberInfo.Name == _propertySelector.Body.As<MemberExpression>().Member.Name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Text.Json.Serialization.Metadata; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Volo.Abp.Json.SystemTextJson.Modifiers; |
|||
|
|||
public static class AbpIncludeExtraPropertiesModifiers |
|||
{ |
|||
public static void Modify(JsonTypeInfo jsonTypeInfo) |
|||
{ |
|||
if (typeof(IHasExtraProperties).IsAssignableFrom(jsonTypeInfo.Type)) |
|||
{ |
|||
var propertyJsonInfo = jsonTypeInfo.Properties |
|||
.Where(x => x.AttributeProvider is MemberInfo) |
|||
.FirstOrDefault(x => |
|||
x.PropertyType == typeof(ExtraPropertyDictionary) && |
|||
x.AttributeProvider.As<MemberInfo>().Name == nameof(ExtensibleObject.ExtraProperties) && |
|||
x.Set == null); |
|||
|
|||
if (propertyJsonInfo != null) |
|||
{ |
|||
propertyJsonInfo.Set = (obj, value) => |
|||
{ |
|||
ObjectHelper.TrySetProperty(obj.As<IHasExtraProperties>(), x => x.ExtraProperties, () => value); |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue