diff --git a/common.props b/common.props
index 6bad8e8068..6ce5cae828 100644
--- a/common.props
+++ b/common.props
@@ -19,4 +19,13 @@
runtime; build; native; contentfiles; analyzers
+
+
+
+
+ true
+ Never
+
+
+
diff --git a/docs/en/API/Swagger-Integration.md b/docs/en/API/Swagger-Integration.md
new file mode 100644
index 0000000000..48ce252940
--- /dev/null
+++ b/docs/en/API/Swagger-Integration.md
@@ -0,0 +1,140 @@
+# Swagger Integration
+
+[Swagger (OpenAPI)](https://swagger.io/) is a language-agnostic specification for describing REST APIs. It allows both computers and humans to understand the capabilities of a REST API without direct access to the source code. Its main goals are to:
+
+- Minimize the amount of work needed to connect decoupled services.
+- Reduce the amount of time needed to accurately document a service.
+
+ABP Framework offers a prebuilt module for full Swagger integration with small configurations.
+
+## Installation
+
+> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually.
+
+If installation is needed, it is suggested to use the [ABP CLI](CLI.md) to install this package.
+
+### Using the ABP CLI
+
+Open a command line window in the folder of the `Web` or `HttpApi.Host` project (.csproj file) and type the following command:
+
+```bash
+abp add-package Volo.Abp.Swashbuckle
+```
+
+### Manual Installation
+
+If you want to manually install;
+
+1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project:
+
+ `Install-Package Volo.Abp.Swashbuckle`
+
+2. Add the `AbpSwashbuckleModule` to the dependency list of your module:
+
+ ```csharp
+ [DependsOn(
+ //...other dependencies
+ typeof(AbpSwashbuckleModule) // <-- Add module dependency like that
+ )]
+ public class YourModule : AbpModule
+ {
+ }
+ ```
+
+## Configuration
+
+First, we need to use `AddAbpSwaggerGen` extension to configure Swagger in `ConfigureServices` method of our module.
+
+```csharp
+public override void ConfigureServices(ServiceConfigurationContext context)
+{
+ var services = contex.Services;
+
+ //... other configarations.
+
+ services.AddAbpSwaggerGen(
+ options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
+ options.DocInclusionPredicate((docName, description) => true);
+ options.CustomSchemaIds(type => type.FullName);
+ }
+ );
+}
+```
+
+Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module.
+
+```csharp
+public override void OnApplicationInitialization(ApplicationInitializationContext context)
+{
+ var app = context.GetApplicationBuilder();
+
+ //... other configarations.
+
+ app.UseAbpSwaggerUI(options =>
+ {
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API");
+ });
+
+ //... other configarations.
+}
+```
+
+## Using Swagger with OAUTH
+
+For non MVC/Tiered applications, we need to configure Swagger with OAUTH to handle authorization.
+
+> ABP Framework uses IdentityServer by default. To get more information about IDS, check this [documentation](Modules/IdentityServer.md).
+
+
+
+To do that, we need to use `AddAbpSwaggerGenWithOAuth` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module.
+
+```csharp
+public override void ConfigureServices(ServiceConfigurationContext context)
+{
+ var services = contex.Services;
+
+ //... other configarations.
+
+ services.AddAbpSwaggerGenWithOAuth(
+ "https://localhost:44341", // authority issuer
+ new Dictionary //
+ { // scopes
+ {"Test", "Test API"} //
+ }, //
+ options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
+ options.DocInclusionPredicate((docName, description) => true);
+ options.CustomSchemaIds(type => type.FullName);
+ }
+ );
+}
+```
+
+Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module.
+
+> Do not forget to set `OAuthClientId` and `OAuthClientSecret`.
+
+
+```csharp
+public override void OnApplicationInitialization(ApplicationInitializationContext context)
+{
+ var app = context.GetApplicationBuilder();
+
+ //... other configarations.
+
+ app.UseAbpSwaggerUI(options =>
+ {
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API");
+
+ var configuration = context.ServiceProvider.GetRequiredService();
+ options.OAuthClientId("Test_Swagger"); // clientId
+ options.OAuthClientSecret("1q2w3e*"); // clientSecret
+ });
+
+ //... other configarations.
+}
+```
\ No newline at end of file
diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md
index cfa2e12a6b..a690d9d17b 100644
--- a/docs/en/Background-Jobs-Hangfire.md
+++ b/docs/en/Background-Jobs-Hangfire.md
@@ -79,3 +79,26 @@ After you have installed these NuGet packages, you need to configure your projec
}
````
+
+### Dashboard Authorization
+
+Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard.
+To make it secure by default, only local requests are allowed, however you can change this by following the [official documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html) of Hangfire.
+
+You can integrate the Hangfire dashboard to [ABP authorization system](Authorization.md) using the **AbpHangfireAuthorizationFilter**
+class. This class is defined in the `Volo.Abp.Hangfire` package. The following example, checks if the current user is logged in to the application:
+
+ app.UseHangfireDashboard("/hangfire", new DashboardOptions
+ {
+ AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter() }
+ });
+
+If you want to require an additional permission, you can pass it into the constructor as below:
+
+ app.UseHangfireDashboard("/hangfire", new DashboardOptions
+ {
+ AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter("MyHangFireDashboardPermissionName") }
+ });
+
+**Important**: `UseHangfireDashboard` should be called after the authentication middleware in your `Startup` class (probably at the last line). Otherwise,
+authorization will always fail!
diff --git a/docs/en/Swagger.md b/docs/en/Swagger.md
deleted file mode 100644
index 1fda8a0cd7..0000000000
--- a/docs/en/Swagger.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Swagger UI Integration
-
-TODO
\ No newline at end of file
diff --git a/docs/en/Validation.md b/docs/en/Validation.md
index c4821adf65..1d24c5e2a6 100644
--- a/docs/en/Validation.md
+++ b/docs/en/Validation.md
@@ -149,8 +149,8 @@ Once ABP determines a validation error, it throws an exception of type `AbpValid
In addition to the automatic validation, you may want to manually validate an object. In this case, [inject](Dependency-Injection.md) and use the `IObjectValidator` service:
-* `Validate` method validates the given object based on the validation rules and throws an `AbpValidationException` if it is not in a valid state.
-* `GetErrors` doesn't throw an exception, but only returns the validation errors.
+* `ValidateAsync` method validates the given object based on the validation rules and throws an `AbpValidationException` if it is not in a valid state.
+* `GetErrorsAsync` doesn't throw an exception, but only returns the validation errors.
`IObjectValidator` is implemented by the `ObjectValidator` by default. `ObjectValidator` is extensible; you can implement `IObjectValidationContributor` interface to contribute a custom logic. Example:
@@ -158,13 +158,14 @@ In addition to the automatic validation, you may want to manually validate an ob
public class MyObjectValidationContributor
: IObjectValidationContributor, ITransientDependency
{
- public void AddErrors(ObjectValidationContext context)
+ public Task AddErrorsAsync(ObjectValidationContext context)
{
//Get the validating object
var obj = context.ValidatingObject;
//Add the validation errors if available
context.Errors.Add(...);
+ return Task.CompletedTask;
}
}
````
diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json
index 5a2b18d365..398da988e4 100644
--- a/docs/en/docs-nav.json
+++ b/docs/en/docs-nav.json
@@ -578,6 +578,10 @@
"path": "API/Application-Configuration.md"
}
]
+ },
+ {
+ "text": "Swagger Integration",
+ "path": "API/Swagger-Integration.md"
}
]
},
diff --git a/docs/zh-Hans/Validation.md b/docs/zh-Hans/Validation.md
index f6b2daf31f..6e46b07aed 100644
--- a/docs/zh-Hans/Validation.md
+++ b/docs/zh-Hans/Validation.md
@@ -130,8 +130,8 @@ namespace Acme.BookStore
除了自动验证你可能需要手动验证对象,这种情况下[注入](Dependency-Injection.md)并使用 `IObjectValidator` 服务:
-* `Validate` 方法根据验证规则验证给定对象,如果对象没有被验证通过会抛出 `AbpValidationException` 异常.
-* `GetErrors` 不会抛出异常,只返回验证错误.
+* `ValidateAsync` 方法根据验证规则验证给定对象,如果对象没有被验证通过会抛出 `AbpValidationException` 异常.
+* `GetErrorsAsync` 不会抛出异常,只返回验证错误.
`IObjectValidator` 默认由 `ObjectValidator` 实现. `ObjectValidator`是可扩展的; 可以实现`IObjectValidationContributor`接口提供自定义逻辑.
示例 :
@@ -140,13 +140,14 @@ namespace Acme.BookStore
public class MyObjectValidationContributor
: IObjectValidationContributor, ITransientDependency
{
- public void AddErrors(ObjectValidationContext context)
+ public Task AddErrorsAsync(ObjectValidationContext context)
{
//Get the validating object
var obj = context.ValidatingObject;
//Add the validation errors if available
context.Errors.Add(...);
+ return Task.CompletedTask;
}
}
````
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
index 273f011f41..62d490d64d 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
@@ -576,11 +576,12 @@ namespace Volo.Abp.Cli.ProjectModification
return;
}
- var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj"));
+ var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj"))
+ ?? projectFiles.FirstOrDefault(p => p.EndsWith(".EntityFrameworkCore.csproj")) ;
if (dbMigrationsProject == null)
{
- Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project.");
+ Logger.LogDebug("Solution doesn't have a Migrations project.");
if (!skipDbMigrations)
{
diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs
index ccc290f354..e2d0f39372 100644
--- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs
+++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs
@@ -2,6 +2,7 @@ using FluentValidation;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;
@@ -17,7 +18,7 @@ namespace Volo.Abp.FluentValidation
_serviceProvider = serviceProvider;
}
- public void AddErrors(ObjectValidationContext context)
+ public virtual async Task AddErrorsAsync(ObjectValidationContext context)
{
var serviceType = typeof(IValidator<>).MakeGenericType(context.ValidatingObject.GetType());
var validator = _serviceProvider.GetService(serviceType) as IValidator;
@@ -26,7 +27,7 @@ namespace Volo.Abp.FluentValidation
return;
}
- var result = validator.Validate((IValidationContext) Activator.CreateInstance(
+ var result = await validator.ValidateAsync((IValidationContext) Activator.CreateInstance(
typeof(ValidationContext<>).MakeGenericType(context.ValidatingObject.GetType()),
context.ValidatingObject));
diff --git a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj
index 4ec9d0a3b5..6b598d0edd 100644
--- a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj
+++ b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj
@@ -19,6 +19,7 @@
+
diff --git a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs
new file mode 100644
index 0000000000..15fbb4f60c
--- /dev/null
+++ b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Threading.Tasks;
+using Hangfire.Dashboard;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Users;
+
+namespace Volo.Abp.Hangfire
+{
+ public class AbpHangfireAuthorizationFilter : IDashboardAsyncAuthorizationFilter
+ {
+ private readonly string _requiredPermissionName;
+
+ public AbpHangfireAuthorizationFilter(string requiredPermissionName = null)
+ {
+ _requiredPermissionName = requiredPermissionName;
+ }
+
+ public async Task AuthorizeAsync(DashboardContext context)
+ {
+ if (!IsLoggedIn(context))
+ {
+ return false;
+ }
+
+ if (_requiredPermissionName.IsNullOrEmpty())
+ {
+ return true;
+ }
+
+ return await IsPermissionGrantedAsync(context, _requiredPermissionName);
+ }
+
+ private static bool IsLoggedIn(DashboardContext context)
+ {
+ var currentUser = context.GetHttpContext().RequestServices.GetRequiredService();
+ return currentUser.IsAuthenticated;
+ }
+
+ private static async Task IsPermissionGrantedAsync(DashboardContext context, string requiredPermissionName)
+ {
+ var permissionChecker = context.GetHttpContext().RequestServices.GetRequiredService();
+ return await permissionChecker.IsGrantedAsync(requiredPermissionName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
index d69edb0b5d..1628d2f37f 100644
--- a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
+++ b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
@@ -1,10 +1,12 @@
using Hangfire;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Volo.Abp.Authorization;
using Volo.Abp.Modularity;
namespace Volo.Abp.Hangfire
{
+ [DependsOn(typeof(AbpAuthorizationAbstractionsModule))]
public class AbpHangfireModule : AbpModule
{
private BackgroundJobServer _backgroundJobServer;
diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
index b94a5a339d..df890812bb 100644
--- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
+++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
@@ -174,7 +174,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
var response = await client.SendAsync(
requestMessage,
HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/,
- GetCancellationToken()
+ GetCancellationToken(invocation)
);
if (!response.IsSuccessStatusCode)
@@ -306,8 +306,18 @@ namespace Volo.Abp.Http.Client.DynamicProxying
return input;
}
- protected virtual CancellationToken GetCancellationToken()
+ protected virtual CancellationToken GetCancellationToken(IAbpMethodInvocation invocation)
{
+ var cancellationTokenArg = invocation.Arguments.LastOrDefault(x => x is CancellationToken);
+ if (cancellationTokenArg != null)
+ {
+ var cancellationToken = (CancellationToken) cancellationTokenArg;
+ if (cancellationToken != default)
+ {
+ return cancellationToken;
+ }
+ }
+
return CancellationTokenProvider.Token;
}
}
diff --git a/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj b/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj
index cae6301daf..83cea09848 100644
--- a/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj
+++ b/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj
@@ -18,6 +18,7 @@
+
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs
index 912f68e4af..15e367cefc 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@@ -26,9 +27,10 @@ namespace Volo.Abp.Validation
Options = options.Value;
}
- public void AddErrors(ObjectValidationContext context)
+ public Task AddErrorsAsync(ObjectValidationContext context)
{
ValidateObjectRecursively(context.Errors, context.ValidatingObject, currentDepth: 1);
+ return Task.CompletedTask;
}
protected virtual void ValidateObjectRecursively(List errors, object validatingObject, int currentDepth)
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs
index fd413bc498..89eec62116 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs
@@ -1,7 +1,9 @@
-namespace Volo.Abp.Validation
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Validation
{
public interface IMethodInvocationValidator
{
- void Validate(MethodInvocationValidationContext context);
+ Task ValidateAsync(MethodInvocationValidationContext context);
}
}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs
index ca50901bcd..45d0ddcd9b 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs
@@ -1,7 +1,9 @@
-namespace Volo.Abp.Validation
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Validation
{
public interface IObjectValidationContributor
{
- void AddErrors(ObjectValidationContext context);
+ Task AddErrorsAsync(ObjectValidationContext context);
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs
index 0ce723bab8..1b7c5881ee 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs
@@ -1,20 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.Threading.Tasks;
namespace Volo.Abp.Validation
{
public interface IObjectValidator
{
- void Validate(
+ Task ValidateAsync(
object validatingObject,
string name = null,
bool allowNull = false
);
- List GetErrors(
+ Task> GetErrorsAsync(
object validatingObject,
string name = null,
bool allowNull = false
);
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs
index 584f787a6e..3aeaf57fc7 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
@@ -16,7 +17,7 @@ namespace Volo.Abp.Validation
_objectValidator = objectValidator;
}
- public virtual void Validate(MethodInvocationValidationContext context)
+ public virtual async Task ValidateAsync(MethodInvocationValidationContext context)
{
Check.NotNull(context, nameof(context));
@@ -46,7 +47,7 @@ namespace Volo.Abp.Validation
ThrowValidationError(context);
}
- AddMethodParameterValidationErrors(context);
+ await AddMethodParameterValidationErrorsAsync(context);
if (context.Errors.Any())
{
@@ -60,7 +61,7 @@ namespace Volo.Abp.Validation
{
return false;
}
-
+
if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(context.Method) != null)
{
return true;
@@ -82,22 +83,22 @@ namespace Volo.Abp.Validation
);
}
- protected virtual void AddMethodParameterValidationErrors(MethodInvocationValidationContext context)
+ protected virtual async Task AddMethodParameterValidationErrorsAsync(MethodInvocationValidationContext context)
{
for (var i = 0; i < context.Parameters.Length; i++)
{
- AddMethodParameterValidationErrors(context, context.Parameters[i], context.ParameterValues[i]);
+ await AddMethodParameterValidationErrorsAsync(context, context.Parameters[i], context.ParameterValues[i]);
}
}
- protected virtual void AddMethodParameterValidationErrors(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue)
+ protected virtual async Task AddMethodParameterValidationErrorsAsync(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue)
{
var allowNulls = parameterInfo.IsOptional ||
parameterInfo.IsOut ||
TypeHelper.IsPrimitiveExtended(parameterInfo.ParameterType, includeEnums: true);
context.Errors.AddRange(
- _objectValidator.GetErrors(
+ await _objectValidator.GetErrorsAsync(
parameterValue,
parameterInfo.Name,
allowNulls
@@ -105,4 +106,4 @@ namespace Volo.Abp.Validation
);
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs
index d8e0c60b65..8e13d72938 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
@@ -18,9 +19,9 @@ namespace Volo.Abp.Validation
Options = options.Value;
}
- public virtual void Validate(object validatingObject, string name = null, bool allowNull = false)
+ public virtual async Task ValidateAsync(object validatingObject, string name = null, bool allowNull = false)
{
- var errors = GetErrors(validatingObject, name, allowNull);
+ var errors = await GetErrorsAsync(validatingObject, name, allowNull);
if (errors.Any())
{
@@ -31,7 +32,7 @@ namespace Volo.Abp.Validation
}
}
- public virtual List GetErrors(object validatingObject, string name = null, bool allowNull = false)
+ public virtual async Task> GetErrorsAsync(object validatingObject, string name = null, bool allowNull = false)
{
if (validatingObject == null)
{
@@ -58,7 +59,7 @@ namespace Volo.Abp.Validation
{
var contributor = (IObjectValidationContributor)
scope.ServiceProvider.GetRequiredService(contributorType);
- contributor.AddErrors(context);
+ await contributor.AddErrorsAsync(context);
}
}
diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs
index b4ce642471..0a0f4fcf5f 100644
--- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs
+++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs
@@ -15,13 +15,13 @@ namespace Volo.Abp.Validation
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
- Validate(invocation);
+ await ValidateAsync(invocation);
await invocation.ProceedAsync();
}
- protected virtual void Validate(IAbpMethodInvocation invocation)
+ protected virtual async Task ValidateAsync(IAbpMethodInvocation invocation)
{
- _methodInvocationValidator.Validate(
+ await _methodInvocationValidator.ValidateAsync(
new MethodInvocationValidationContext(
invocation.TargetObject,
invocation.Method,
diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs
index a93eabda1d..6263d03ddd 100644
--- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs
+++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs
@@ -39,7 +39,8 @@ namespace Volo.Abp.FluentValidation
},
MyMethodInput3 = new MyMethodInput3
{
- MyStringValue3 = "ccc"
+ MyStringValue3 = "ccc",
+ MyBoolValue3 = true
}
});
@@ -62,12 +63,13 @@ namespace Volo.Abp.FluentValidation
},
MyMethodInput3 = new MyMethodInput3
{
- MyStringValue3 = "c"
+ MyStringValue3 = "c",
+ MyBoolValue3 = false
}
}
)
);
-
+
exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyStringValue"));
exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyMethodInput2.MyStringValue2"));
exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyMethodInput3.MyStringValue3"));
@@ -100,7 +102,7 @@ namespace Volo.Abp.FluentValidation
output.ShouldBe("444");
}
-
+
[DependsOn(typeof(AbpAutofacModule))]
[DependsOn(typeof(AbpFluentValidationModule))]
public class TestModule : AbpModule
@@ -162,6 +164,8 @@ namespace Volo.Abp.FluentValidation
{
public string MyStringValue3 { get; set; }
+
+ public bool MyBoolValue3 { get; set; }
}
public class MyMethodInput4
@@ -175,7 +179,8 @@ namespace Volo.Abp.FluentValidation
{
RuleFor(x => x.MyStringValue).Equal("aaa");
RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb");
- RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3Validator());
+ RuleFor(x => x.MyMethodInput3).SetValidator(new MyMethodInput3Validator());
+ RuleFor(x => x.MyMethodInput3).SetValidator(new MyMethodInput3AsyncValidator());
}
}
@@ -194,5 +199,15 @@ namespace Volo.Abp.FluentValidation
RuleFor(x => x.MyStringValue3).Equal("ccc");
}
}
+
+ public class MyMethodInput3AsyncValidator : MethodInputBaseValidator
+ {
+ public MyMethodInput3AsyncValidator()
+ {
+ RuleFor(x => x.MyStringValue3).Equal("ccc");
+
+ RuleFor(x => x.MyBoolValue3).MustAsync((myBookValue3, cancellation) => Task.FromResult(myBookValue3));
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
index 23694e4e72..d1c1275db7 100644
--- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
+++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.Http.DynamicProxying
@@ -36,5 +37,7 @@ namespace Volo.Abp.Http.DynamicProxying
Task PatchValueWithHeaderAndQueryStringAsync(string headerValue, string qsValue);
Task DeleteByIdAsync(int id);
+
+ Task AbortRequestAsync(CancellationToken cancellationToken = default);
}
}
diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
index e18c2f8f4a..782b839626 100644
--- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
+++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
-using Volo.Abp.Application.Services;
using Volo.Abp.AspNetCore.Mvc;
-using Volo.Abp.UI;
namespace Volo.Abp.Http.DynamicProxying
{
@@ -129,6 +128,14 @@ namespace Volo.Abp.Http.DynamicProxying
{
return Task.FromResult(id + 1);
}
+
+ [HttpGet]
+ [Route("abort-request")]
+ public async Task AbortRequestAsync(CancellationToken cancellationToken = default)
+ {
+ await Task.Delay(100, cancellationToken);
+ return "AbortRequestAsync";
+ }
}
public class Car
diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs
index bd35016989..8df3e94b3c 100644
--- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs
+++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs
@@ -1,10 +1,10 @@
using System;
+using System.Net.Http;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Localization;
using Shouldly;
using Volo.Abp.Http.Client;
-using Volo.Abp.Http.Localization;
using Volo.Abp.Localization;
using Xunit;
@@ -159,5 +159,17 @@ namespace Volo.Abp.Http.DynamicProxying
(await _controller.DeleteByIdAsync(42)).ShouldBe(43);
}
+ [Fact]
+ public async Task AbortRequestAsync()
+ {
+ var cts = new CancellationTokenSource();
+ cts.CancelAfter(10);
+
+ var result = await _controller.AbortRequestAsync(default);
+ result.ShouldBe("AbortRequestAsync");
+
+ var exception = await Assert.ThrowsAsync(async () => await _controller.AbortRequestAsync(cts.Token));
+ exception.InnerException.InnerException.Message.ShouldBe("The client aborted the request.");
+ }
}
}
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs
index 6abd112533..6b911c6e36 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs
@@ -25,6 +25,12 @@ namespace Volo.Docs.Documents
string version,
CancellationToken cancellationToken = default);
+ Task> GetListAsync(
+ Guid? projectId,
+ string version,
+ string name,
+ CancellationToken cancellationToken = default);
+
Task> GetAllAsync(
Guid? projectId,
string name,
@@ -67,4 +73,4 @@ namespace Volo.Docs.Documents
Task GetAsync(Guid id, CancellationToken cancellationToken = default);
}
-}
\ No newline at end of file
+}
diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs
index e44c52c61c..a322120c3d 100644
--- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs
+++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs
@@ -38,6 +38,15 @@ namespace Volo.Docs.Documents
return await (await GetDbSetAsync()).Where(d => d.ProjectId == projectId).ToListAsync(cancellationToken: cancellationToken);
}
+ public async Task> GetListAsync(Guid? projectId, string version, string name, CancellationToken cancellationToken = default)
+ {
+ return await (await GetDbSetAsync())
+ .WhereIf(version != null, x => x.Version == version)
+ .WhereIf(name != null, x => x.Name == name)
+ .WhereIf(projectId.HasValue, x => x.ProjectId == projectId)
+ .ToListAsync(cancellationToken: cancellationToken);
+ }
+
public async Task> GetAllAsync(
Guid? projectId,
string name,
diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs
index 955319d848..3a57330557 100644
--- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs
+++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs
@@ -57,6 +57,16 @@ namespace Volo.Docs.Documents
x.Version == version, cancellationToken: cancellationToken);
}
+ public async Task> GetListAsync(Guid? projectId, string version, string name, CancellationToken cancellationToken = default)
+ {
+ return await (await GetMongoQueryableAsync(cancellationToken))
+ .WhereIf(version != null, x => x.Version == version)
+ .WhereIf(name != null, x => x.Name == name)
+ .WhereIf(projectId.HasValue, x => x.ProjectId == projectId)
+ .As>()
+ .ToListAsync(cancellationToken);
+ }
+
public async Task> GetAllAsync(
Guid? projectId,
string name,