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; |
||||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|
||||
using Microsoft.Extensions.Options; |
using Microsoft.Extensions.Options; |
||||
using Microsoft.Extensions.ObjectPool; |
using Volo.Abp.Json.SystemTextJson; |
||||
using Volo.Abp.Json; |
using Volo.Abp.Json.SystemTextJson.JsonConverters; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.Json; |
namespace Volo.Abp.AspNetCore.Mvc.Json; |
||||
|
|
||||
public static class MvcCoreBuilderExtensions |
public static class MvcCoreBuilderExtensions |
||||
{ |
{ |
||||
public static IMvcCoreBuilder AddAbpHybridJson(this IMvcCoreBuilder builder) |
public static IMvcCoreBuilder AddAbpJson(this IMvcCoreBuilder builder) |
||||
{ |
{ |
||||
var abpJsonOptions = builder.Services.ExecutePreConfiguredActions<AbpJsonOptions>(); |
builder.Services.AddOptions<JsonOptions>() |
||||
if (!abpJsonOptions.UseHybridSerializer) |
.Configure<IServiceProvider>((options, serviceProvider) => |
||||
{ |
{ |
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>()); |
options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; |
||||
builder.AddNewtonsoftJson(); |
options.JsonSerializerOptions.AllowTrailingCommas = true; |
||||
return builder; |
|
||||
} |
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; |
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 Newtonsoft.Json; |
||||
using Volo.Abp.Collections; |
|
||||
|
|
||||
namespace Volo.Abp.Json.Newtonsoft; |
namespace Volo.Abp.Json.Newtonsoft; |
||||
|
|
||||
public class AbpNewtonsoftJsonSerializerOptions |
public class AbpNewtonsoftJsonSerializerOptions |
||||
{ |
{ |
||||
public ITypeList<JsonConverter> Converters { get; } |
public JsonSerializerSettings JsonSerializerSettings { get; } |
||||
|
|
||||
public AbpNewtonsoftJsonSerializerOptions() |
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