38 changed files with 1365 additions and 0 deletions
@ -0,0 +1,138 @@
|
||||
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\Projects\MicroService\CRM\Vue\abp-next-admin\aspnet-core codebase based on best match to current usage at 2022-01-07 |
||||
# There already existed an .editorconfig file in this directory. Copy rules from this .editorconfig.inferred file to the existing .editorconfig file as desired to have them take effect at this location. |
||||
# You can modify the rules from these initially generated values to suit your own policies |
||||
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference |
||||
[*.cs] |
||||
|
||||
|
||||
#Core editorconfig formatting - indentation |
||||
|
||||
#use soft tabs (spaces) for indentation |
||||
indent_style = space |
||||
|
||||
#Formatting - indentation options |
||||
|
||||
#indent switch case contents. |
||||
csharp_indent_case_contents = true |
||||
#indent switch labels |
||||
csharp_indent_switch_labels = true |
||||
|
||||
#Formatting - new line options |
||||
|
||||
#place catch statements on a new line |
||||
csharp_new_line_before_catch = true |
||||
#place else statements on a new line |
||||
csharp_new_line_before_else = true |
||||
#require members of anonymous types to be on separate lines |
||||
csharp_new_line_before_members_in_anonymous_types = true |
||||
#require members of object initializers to be on the same line |
||||
csharp_new_line_before_members_in_object_initializers = false |
||||
#require braces to be on a new line for methods, control_blocks, types, lambdas, object_collection_array_initializers, anonymous_methods, and anonymous_types (also known as "Allman" style) |
||||
csharp_new_line_before_open_brace = methods, control_blocks, types, lambdas, object_collection_array_initializers, anonymous_methods, anonymous_types |
||||
|
||||
#Formatting - organize using options |
||||
|
||||
#do not place System.* using directives before other using directives |
||||
dotnet_sort_system_directives_first = false |
||||
|
||||
#Formatting - spacing options |
||||
|
||||
#require NO space between a cast and the value |
||||
csharp_space_after_cast = false |
||||
#require a space before the colon for bases or interfaces in a type declaration |
||||
csharp_space_after_colon_in_inheritance_clause = true |
||||
#require a space after a keyword in a control flow statement such as a for loop |
||||
csharp_space_after_keywords_in_control_flow_statements = true |
||||
#require a space before the colon for bases or interfaces in a type declaration |
||||
csharp_space_before_colon_in_inheritance_clause = true |
||||
#remove space within empty argument list parentheses |
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false |
||||
#remove space between method call name and opening parenthesis |
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false |
||||
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call |
||||
csharp_space_between_method_call_parameter_list_parentheses = false |
||||
#remove space within empty parameter list parentheses for a method declaration |
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false |
||||
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. |
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false |
||||
|
||||
#Formatting - wrapping options |
||||
|
||||
#leave code block on single line |
||||
csharp_preserve_single_line_blocks = true |
||||
#leave statements and member declarations on the same line |
||||
csharp_preserve_single_line_statements = true |
||||
|
||||
#Style - Code block preferences |
||||
|
||||
#prefer curly braces even for one line of code |
||||
csharp_prefer_braces = true:suggestion |
||||
|
||||
#Style - expression bodied member options |
||||
|
||||
#prefer block bodies for constructors |
||||
csharp_style_expression_bodied_constructors = false:suggestion |
||||
#prefer block bodies for methods |
||||
csharp_style_expression_bodied_methods = false:suggestion |
||||
#prefer expression-bodied members for properties |
||||
csharp_style_expression_bodied_properties = true:suggestion |
||||
|
||||
#Style - expression level options |
||||
|
||||
#prefer out variables to be declared inline in the argument list of a method call when possible |
||||
csharp_style_inlined_variable_declaration = true:suggestion |
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them |
||||
dotnet_style_predefined_type_for_member_access = true:suggestion |
||||
|
||||
#Style - Expression-level preferences |
||||
|
||||
#prefer default over default(T) |
||||
csharp_prefer_simple_default_expression = true:suggestion |
||||
#prefer objects to be initialized using object initializers when possible |
||||
dotnet_style_object_initializer = true:suggestion |
||||
#prefer inferred anonymous type member names |
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion |
||||
|
||||
#Style - implicit and explicit types |
||||
|
||||
#prefer var over explicit type in all cases, unless overridden by another code style rule |
||||
csharp_style_var_elsewhere = true:suggestion |
||||
#prefer var is used to declare variables with built-in system types such as int |
||||
csharp_style_var_for_built_in_types = true:suggestion |
||||
#prefer var when the type is already mentioned on the right-hand side of a declaration expression |
||||
csharp_style_var_when_type_is_apparent = true:suggestion |
||||
|
||||
#Style - language keyword and framework type options |
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them |
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion |
||||
|
||||
#Style - Miscellaneous preferences |
||||
|
||||
#prefer anonymous functions over local functions |
||||
csharp_style_pattern_local_over_anonymous_function = false:suggestion |
||||
|
||||
#Style - modifier options |
||||
|
||||
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. |
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion |
||||
|
||||
#Style - Modifier preferences |
||||
|
||||
#when this rule is set to a list of modifiers, prefer the specified ordering. |
||||
csharp_preferred_modifier_order = public,private,protected,internal,async,virtual,readonly,static,override,abstract:suggestion |
||||
|
||||
#Style - Pattern matching |
||||
|
||||
#prefer pattern matching instead of is expression with type casts |
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion |
||||
|
||||
#Style - qualification options |
||||
|
||||
#prefer fields not to be prefaced with this. or Me. in Visual Basic |
||||
dotnet_style_qualification_for_field = false:suggestion |
||||
#prefer methods not to be prefaced with this. or Me. in Visual Basic |
||||
dotnet_style_qualification_for_method = false:suggestion |
||||
#prefer properties not to be prefaced with this. or Me. in Visual Basic |
||||
dotnet_style_qualification_for_property = false:suggestion |
||||
csharp_style_namespace_declarations=file_scoped:silent |
@ -0,0 +1,5 @@
|
||||
<Project> |
||||
<PropertyGroup> |
||||
<VoloAbpVersion>5.1.4</VoloAbpVersion> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,48 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
# Visual Studio Version 17 |
||||
VisualStudioVersion = 17.1.32328.378 |
||||
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{F5F5D604-531B-4B57-A88E-C9C5CEEC55D7}" |
||||
EndProject |
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "services", "services", "{BBE3B270-DF4F-47F5-9705-89FCFDE2779F}" |
||||
EndProject |
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{2A768109-31B7-4C52-928C-3023DAB9F254}" |
||||
EndProject |
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0E7AC09-318B-4119-A09D-237961F1965F}" |
||||
EndProject |
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mvc", "mvc", "{21B8099B-881D-40AE-888E-FC94E66D90C0}" |
||||
EndProject |
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Wrapper", "modules\common\Sanhe.Abp.Wrapper\Sanhe.Abp.Wrapper.csproj", "{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0}" |
||||
EndProject |
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.AspNetCore.Mvc.Wrapper", "modules\mvc\Sanhe.Abp.AspNetCore.Mvc.Wrapper\Sanhe.Abp.AspNetCore.Mvc.Wrapper.csproj", "{06F1C7AC-3A43-4210-8126-8DA0089A39EE}" |
||||
EndProject |
||||
Global |
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
Debug|Any CPU = Debug|Any CPU |
||||
Release|Any CPU = Release|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
{06F1C7AC-3A43-4210-8126-8DA0089A39EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
{06F1C7AC-3A43-4210-8126-8DA0089A39EE}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
{06F1C7AC-3A43-4210-8126-8DA0089A39EE}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
{06F1C7AC-3A43-4210-8126-8DA0089A39EE}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(SolutionProperties) = preSolution |
||||
HideSolutionNode = FALSE |
||||
EndGlobalSection |
||||
GlobalSection(NestedProjects) = preSolution |
||||
{2A768109-31B7-4C52-928C-3023DAB9F254} = {F5F5D604-531B-4B57-A88E-C9C5CEEC55D7} |
||||
{E0E7AC09-318B-4119-A09D-237961F1965F} = {BBE3B270-DF4F-47F5-9705-89FCFDE2779F} |
||||
{21B8099B-881D-40AE-888E-FC94E66D90C0} = {F5F5D604-531B-4B57-A88E-C9C5CEEC55D7} |
||||
{8C1439CF-4968-4A95-9E9F-CE8F1BBED0A0} = {2A768109-31B7-4C52-928C-3023DAB9F254} |
||||
{06F1C7AC-3A43-4210-8126-8DA0089A39EE} = {21B8099B-881D-40AE-888E-FC94E66D90C0} |
||||
EndGlobalSection |
||||
GlobalSection(ExtensibilityGlobals) = postSolution |
||||
SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD} |
||||
EndGlobalSection |
||||
EndGlobal |
@ -0,0 +1,9 @@
|
||||
<Project> |
||||
<PropertyGroup> |
||||
<LangVersion>latest</LangVersion> |
||||
<Version>5.1.4</Version> |
||||
<NoWarn>$(NoWarn);CS1591;</NoWarn> |
||||
<RepositoryType>git</RepositoryType> |
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,9 @@
|
||||
<Project> |
||||
<ItemGroup> |
||||
<PackageReference Include="ConfigureAwait.Fody" Version="3.3.1" PrivateAssets="All" /> |
||||
<PackageReference Include="Fody" Version="6.5.3"> |
||||
<PrivateAssets>All</PrivateAssets> |
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
||||
</PackageReference> |
||||
</ItemGroup> |
||||
</Project> |
@ -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,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.ExceptionHandling" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,8 @@
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
public class AbpHttpWrapConsts |
||||
{ |
||||
public const string AbpWrapResult = "_AbpWrapResult"; |
||||
|
||||
public const string AbpDontWrapResult = "_AbpDontWrapResult"; |
||||
} |
@ -0,0 +1,10 @@
|
||||
using Volo.Abp.ExceptionHandling; |
||||
using Volo.Abp.Modularity; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
[DependsOn(typeof(AbpExceptionHandlingModule))] |
||||
public class AbpWrapperModule : AbpModule |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,111 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Net; |
||||
using Volo.Abp.Collections; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
public class AbpWrapperOptions |
||||
{ |
||||
/// <summary> |
||||
/// 未处理异常代码 |
||||
/// 默认: 500 |
||||
/// </summary> |
||||
public string CodeWithUnhandled { get; set; } |
||||
/// <summary> |
||||
/// 是否启用包装器 |
||||
/// </summary> |
||||
public bool IsEnabled { get; set; } |
||||
/// <summary> |
||||
/// 成功时返回代码 |
||||
/// 默认:0 |
||||
/// </summary> |
||||
public string CodeWithSuccess { get; set; } |
||||
/// <summary> |
||||
/// 资源为空时是否提示错误 |
||||
/// 默认: false |
||||
/// </summary> |
||||
public bool ErrorWithEmptyResult { get; set; } |
||||
/// <summary> |
||||
/// 资源为空时返回代码 |
||||
/// 默认:404 |
||||
/// </summary> |
||||
public Func<IServiceProvider, string> CodeWithEmptyResult { get; set; } |
||||
/// <summary> |
||||
/// 资源为空时返回错误消息 |
||||
/// </summary> |
||||
public Func<IServiceProvider, string> MessageWithEmptyResult { get; set; } |
||||
/// <summary> |
||||
/// 包装后的返回状态码 |
||||
/// 默认:200 HttpStatusCode.OK |
||||
/// </summary> |
||||
public HttpStatusCode HttpStatusCode { get; set; } |
||||
/// <summary> |
||||
/// 忽略Url开头类型 |
||||
/// </summary> |
||||
public IList<string> IgnorePrefixUrls { get; } |
||||
/// <summary> |
||||
/// 忽略指定命名空间 |
||||
/// </summary> |
||||
public IList<string> IgnoreNamespaces { get; } |
||||
/// <summary> |
||||
/// 忽略控制器 |
||||
/// </summary> |
||||
public ITypeList IgnoreControllers { get; } |
||||
/// <summary> |
||||
/// 忽略返回值 |
||||
/// </summary> |
||||
public ITypeList IgnoreReturnTypes { get; } |
||||
/// <summary> |
||||
/// 忽略异常 |
||||
/// </summary> |
||||
public ITypeList<Exception> IgnoreExceptions { get; } |
||||
/// <summary> |
||||
/// 忽略接口类型 |
||||
/// </summary> |
||||
public ITypeList IgnoredInterfaces { get; } |
||||
|
||||
internal IDictionary<Type, IExceptionWrapHandler> ExceptionHandles { get; } |
||||
|
||||
public AbpWrapperOptions() |
||||
{ |
||||
CodeWithUnhandled = "500"; |
||||
CodeWithSuccess = "0"; |
||||
HttpStatusCode = HttpStatusCode.OK; |
||||
ErrorWithEmptyResult = false; |
||||
|
||||
IgnorePrefixUrls = new List<string>(); |
||||
IgnoreNamespaces = new List<string>(); |
||||
|
||||
IgnoreControllers = new TypeList(); |
||||
IgnoreReturnTypes = new TypeList(); |
||||
IgnoredInterfaces = new TypeList() |
||||
{ |
||||
typeof(IWrapDisabled) |
||||
}; |
||||
IgnoreExceptions = new TypeList<Exception>(); |
||||
|
||||
CodeWithEmptyResult = (_) => "404"; |
||||
MessageWithEmptyResult = (_) => "Not Found"; |
||||
|
||||
ExceptionHandles = new Dictionary<Type, IExceptionWrapHandler>(); |
||||
} |
||||
|
||||
public void AddHandler<TException>(IExceptionWrapHandler handler) |
||||
where TException : Exception |
||||
{ |
||||
AddHandler(typeof(TException), handler); |
||||
} |
||||
|
||||
public void AddHandler(Type exceptionType, IExceptionWrapHandler handler) |
||||
{ |
||||
ExceptionHandles[exceptionType] = handler; |
||||
} |
||||
|
||||
public IExceptionWrapHandler GetHandler(Type exceptionType) |
||||
{ |
||||
ExceptionHandles.TryGetValue(exceptionType, out IExceptionWrapHandler handler); |
||||
|
||||
return handler; |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Options; |
||||
using System; |
||||
using Volo.Abp.ExceptionHandling; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
public class DefaultExceptionWrapHandler : IExceptionWrapHandler |
||||
{ |
||||
public void Wrap(ExceptionWrapContext context) |
||||
{ |
||||
if (context.Exception is IHasErrorCode exceptionWithErrorCode) |
||||
{ |
||||
string errorCode; |
||||
if (!exceptionWithErrorCode.Code.IsNullOrWhiteSpace() && |
||||
exceptionWithErrorCode.Code.Contains(":")) |
||||
{ |
||||
errorCode = exceptionWithErrorCode.Code.Split(':')[1]; |
||||
} |
||||
else |
||||
{ |
||||
errorCode = exceptionWithErrorCode.Code; |
||||
} |
||||
|
||||
context.WithCode(errorCode); |
||||
} |
||||
|
||||
// 没有处理的异常代码统一用配置代码处理 |
||||
if (context.ErrorInfo.Code.IsNullOrWhiteSpace()) |
||||
{ |
||||
if (context.StatusCode.HasValue) |
||||
{ |
||||
context.WithCode(((int)context.StatusCode).ToString()); |
||||
return; |
||||
} |
||||
var wrapperOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
context.WithCode(wrapperOptions.CodeWithUnhandled); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,64 @@
|
||||
using System; |
||||
using System.Net; |
||||
using Volo.Abp.Http; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 异常包装上下文。 |
||||
/// </summary> |
||||
public class ExceptionWrapContext |
||||
{ |
||||
/// <summary> |
||||
/// 异常 |
||||
/// </summary> |
||||
public Exception Exception { get; } |
||||
/// <summary> |
||||
/// 服务提供器 |
||||
/// </summary> |
||||
public IServiceProvider ServiceProvider { get; } |
||||
/// <summary> |
||||
/// 用于存储有关错误的信息 |
||||
/// </summary> |
||||
public RemoteServiceErrorInfo ErrorInfo { get; } |
||||
/// <summary> |
||||
/// 状态码 |
||||
/// </summary> |
||||
public HttpStatusCode? StatusCode { get; set; } |
||||
|
||||
public ExceptionWrapContext( |
||||
Exception exception, |
||||
RemoteServiceErrorInfo errorInfo, |
||||
IServiceProvider serviceProvider, |
||||
HttpStatusCode? statusCode = null) |
||||
{ |
||||
Exception = exception; |
||||
ErrorInfo = errorInfo; |
||||
ServiceProvider = serviceProvider; |
||||
StatusCode = statusCode; |
||||
} |
||||
|
||||
public ExceptionWrapContext WithCode(string code) |
||||
{ |
||||
ErrorInfo.Code = code; |
||||
return this; |
||||
} |
||||
|
||||
public ExceptionWrapContext WithMessage(string message) |
||||
{ |
||||
ErrorInfo.Message = message; |
||||
return this; |
||||
} |
||||
|
||||
public ExceptionWrapContext WithDetails(string details) |
||||
{ |
||||
ErrorInfo.Details = details; |
||||
return this; |
||||
} |
||||
|
||||
public ExceptionWrapContext WithData(string key, object value) |
||||
{ |
||||
ErrorInfo.Data[key] = value; |
||||
return this; |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
using Microsoft.Extensions.Options; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
using Volo.Abp.DependencyInjection; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
public class ExceptionWrapHandlerFactory : IExceptionWrapHandlerFactory, ITransientDependency |
||||
{ |
||||
private readonly AbpWrapperOptions _options; |
||||
|
||||
public ExceptionWrapHandlerFactory(IOptions<AbpWrapperOptions> options) |
||||
{ |
||||
_options = options.Value; |
||||
} |
||||
|
||||
public IExceptionWrapHandler CreateFor(ExceptionWrapContext context) |
||||
{ |
||||
var exceptionType = context.Exception.GetType(); |
||||
var handler = _options.GetHandler(exceptionType); |
||||
if (handler == null) |
||||
{ |
||||
handler = new DefaultExceptionWrapHandler(); |
||||
_options.AddHandler(exceptionType, handler); |
||||
return handler; |
||||
} |
||||
|
||||
return handler; |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 异常包装处理器接口。 |
||||
/// </summary> |
||||
public interface IExceptionWrapHandler |
||||
{ |
||||
/// <summary> |
||||
/// 包装 |
||||
/// </summary> |
||||
/// <param name="context">异常包装上下文</param> |
||||
void Wrap(ExceptionWrapContext context); |
||||
} |
@ -0,0 +1,6 @@
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
public interface IExceptionWrapHandlerFactory |
||||
{ |
||||
IExceptionWrapHandler CreateFor(ExceptionWrapContext context); |
||||
} |
@ -0,0 +1,9 @@
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 继承此接口表示禁用包装。 |
||||
/// </summary> |
||||
public interface IWrapDisabled |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,12 @@
|
||||
using System; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] |
||||
public class IgnoreWrapResultAttribute : Attribute |
||||
{ |
||||
public IgnoreWrapResultAttribute() |
||||
{ |
||||
|
||||
} |
||||
} |
@ -0,0 +1,26 @@
|
||||
using System; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 返回值包装结构。 |
||||
/// </summary> |
||||
[Serializable] |
||||
public class WrapResult : WrapResult<object> |
||||
{ |
||||
public WrapResult() { } |
||||
|
||||
public WrapResult( |
||||
string code, |
||||
string message, |
||||
string details = null) : base(code, message, details) |
||||
{ |
||||
} |
||||
|
||||
public WrapResult( |
||||
string code, |
||||
object result, |
||||
string message = "OK") : base(code, result, message) |
||||
{ |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
using System; |
||||
|
||||
namespace Sanhe.Abp.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 返回值包装结构。 |
||||
/// </summary> |
||||
/// <typeparam name="TResult"></typeparam> |
||||
[Serializable] |
||||
public class WrapResult<TResult> |
||||
{ |
||||
/// <summary> |
||||
/// 错误代码 |
||||
/// </summary> |
||||
public string Code { get; set; } |
||||
/// <summary> |
||||
/// 错误提示消息 |
||||
/// </summary> |
||||
public string Message { get; set; } |
||||
/// <summary> |
||||
/// 补充消息 |
||||
/// </summary> |
||||
public string Details { get; set; } |
||||
/// <summary> |
||||
/// 返回值 |
||||
/// </summary> |
||||
public TResult Result { get; set; } |
||||
|
||||
public WrapResult() { } |
||||
|
||||
public WrapResult( |
||||
string code, |
||||
string message, |
||||
string details = null) |
||||
{ |
||||
Code = code; |
||||
Message = message; |
||||
Details = details; |
||||
} |
||||
|
||||
public WrapResult( |
||||
string code, |
||||
TResult result, |
||||
string message = "OK") |
||||
{ |
||||
Code = code; |
||||
Result = result; |
||||
Message = message; |
||||
} |
||||
} |
@ -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,37 @@
|
||||
# Sanhe.Abp.AspNetCore.Mvc.Wrapper |
||||
|
||||
返回值包装器 |
||||
|
||||
## 配置使用 |
||||
|
||||
```csharp |
||||
[DependsOn(typeof(AbpAspNetCoreMvcWrapperModule))] |
||||
public class YouProjectModule : AbpModule |
||||
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
||||
Configure<AbpAspNetCoreMvcWrapperOptions>(options => |
||||
{ |
||||
// 启用包装器 |
||||
options.IsEnabled = true; |
||||
}); |
||||
} |
||||
} |
||||
``` |
||||
## 配置项说明 |
||||
|
||||
* AbpAspNetCoreMvcWrapperOptions.IsEnabled 是否包装返回结果,默认: false |
||||
* AbpAspNetCoreMvcWrapperOptions.CodeWithFound 响应成功代码,默认: 0 |
||||
* AbpAspNetCoreMvcWrapperOptions.HttpStatusCode 包装后的Http响应代码, 默认: 200 |
||||
* AbpAspNetCoreMvcWrapperOptions.CodeWithEmptyResult 当返回空对象时返回错误代码,默认: 404 |
||||
* AbpAspNetCoreMvcWrapperOptions.MessageWithEmptyResult 当返回空对象时返回错误消息, 默认: 本地化之后的 NotFound |
||||
|
||||
* AbpAspNetCoreMvcWrapperOptions.IgnorePrefixUrls 指定哪些Url开头的地址不需要处理 |
||||
* AbpAspNetCoreMvcWrapperOptions.IgnoreNamespaces 指定哪些命名空间开头不需要处理 |
||||
* AbpAspNetCoreMvcWrapperOptions.IgnoreControllers 指定哪些控制器不需要处理 |
||||
* AbpAspNetCoreMvcWrapperOptions.IgnoreReturnTypes 指定哪些返回结果类型不需要处理 |
||||
* AbpAspNetCoreMvcWrapperOptions.IgnoreExceptions 指定哪些异常类型不需要处理 |
||||
|
||||
|
||||
## 其他 |
||||
|
@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<Import Project="..\..\..\configureawait.props" /> |
||||
<Import Project="..\..\..\common.props" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net6.0</TargetFramework> |
||||
<RootNamespace /> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<None Remove="Sanhe\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\en.json" /> |
||||
<None Remove="Sanhe\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\zh-Hans.json" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<EmbeddedResource Include="Sanhe\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\en.json" /> |
||||
<EmbeddedResource Include="Sanhe\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\zh-Hans.json" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\..\common\Sanhe.Abp.Wrapper\Sanhe.Abp.Wrapper.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<Folder Include="Microsoft\AspNetCore\Cors\" /> |
||||
<Folder Include="Microsoft\AspNetCore\Mvc\" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,66 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Localization; |
||||
using Sanhe.Abp.AspNetCore.Mvc.Wrapper.Filters; |
||||
using Sanhe.Abp.AspNetCore.Mvc.Wrapper.Localization; |
||||
using Sanhe.Abp.Wrapper; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
||||
using Volo.Abp.AspNetCore.Mvc.ProxyScripting; |
||||
using Volo.Abp.Content; |
||||
using Volo.Abp.Http.Modeling; |
||||
using Volo.Abp.Localization; |
||||
using Volo.Abp.Modularity; |
||||
using Volo.Abp.VirtualFileSystem; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper; |
||||
|
||||
[DependsOn( |
||||
typeof(AbpWrapperModule), |
||||
typeof(AbpAspNetCoreMvcModule))] |
||||
public class AbpAspNetCoreMvcWrapperModule : AbpModule |
||||
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
||||
Configure<AbpVirtualFileSystemOptions>(options => |
||||
{ |
||||
options.FileSets.AddEmbedded<AbpAspNetCoreMvcWrapperModule>(); |
||||
}); |
||||
|
||||
Configure<AbpLocalizationOptions>(options => |
||||
{ |
||||
options.Resources |
||||
.Add<AbpMvcWrapperResource>("en") |
||||
.AddVirtualJson("/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources"); |
||||
}); |
||||
|
||||
Configure<MvcOptions>(mvcOptions => |
||||
{ |
||||
// Wrap Result Filter |
||||
mvcOptions.Filters.AddService(typeof(AbpWrapResultFilter)); |
||||
}); |
||||
|
||||
Configure<AbpWrapperOptions>(options => |
||||
{ |
||||
// 即使重写端点也不包装返回结果 |
||||
// api/abp/api-definition |
||||
options.IgnoreReturnTypes.Add<ApplicationApiDescriptionModel>(); |
||||
// api/abp/application-configuration |
||||
options.IgnoreReturnTypes.Add<ApplicationConfigurationDto>(); |
||||
// 流 |
||||
options.IgnoreReturnTypes.Add<IRemoteStreamContent>(); |
||||
// Abp/ServiceProxyScript |
||||
options.IgnoreControllers.Add<AbpServiceProxyScriptController>(); |
||||
|
||||
// 官方模块不包装结果 |
||||
options.IgnoreNamespaces.Add("Volo.Abp"); |
||||
|
||||
// 返回本地化的 404 错误消息 |
||||
options.MessageWithEmptyResult = (serviceProvider) => |
||||
{ |
||||
var localizer = serviceProvider.GetRequiredService<IStringLocalizer<AbpMvcWrapperResource>>(); |
||||
return localizer["Wrapper:NotFound"]; |
||||
}; |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,88 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Logging.Abstractions; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using System; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.AspNetCore.ExceptionHandling; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; |
||||
using Volo.Abp.Authorization; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.ExceptionHandling; |
||||
using Volo.Abp.Http; |
||||
using Volo.Abp.Json; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.ExceptionHandling |
||||
{ |
||||
[Dependency(ReplaceServices = true)] |
||||
[ExposeServices(typeof(AbpExceptionPageFilter))] |
||||
public class AbpExceptionPageWrapResultFilter : AbpExceptionPageFilter, ITransientDependency |
||||
{ |
||||
protected async override Task HandleAndWrapException(PageHandlerExecutedContext context) |
||||
{ |
||||
var wrapResultChecker = context.GetRequiredService<IWrapResultChecker>(); |
||||
if (!wrapResultChecker.WrapOnException(context)) |
||||
{ |
||||
await base.HandleAndWrapException(context); |
||||
return; |
||||
} |
||||
|
||||
var wrapOptions = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value; |
||||
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>(); |
||||
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, options => |
||||
{ |
||||
options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients; |
||||
options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients; |
||||
}); |
||||
|
||||
var logLevel = context.Exception.GetLogLevel(); |
||||
|
||||
var remoteServiceErrorInfoBuilder = new StringBuilder(); |
||||
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------"); |
||||
remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService<IJsonSerializer>().Serialize(remoteServiceErrorInfo, indented: true)); |
||||
|
||||
var logger = context.GetService<ILogger<AbpExceptionPageWrapResultFilter>>(NullLogger<AbpExceptionPageWrapResultFilter>.Instance); |
||||
logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); |
||||
|
||||
logger.LogException(context.Exception, logLevel); |
||||
|
||||
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception)); |
||||
|
||||
|
||||
if (context.Exception is AbpAuthorizationException) |
||||
{ |
||||
await context.HttpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>() |
||||
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext); |
||||
} |
||||
else |
||||
{ |
||||
var statusCodFinder = context.GetRequiredService<IHttpExceptionStatusCodeFinder>(); |
||||
var exceptionWrapHandler = context.GetRequiredService<IExceptionWrapHandlerFactory>(); |
||||
|
||||
var exceptionWrapContext = new ExceptionWrapContext( |
||||
context.Exception, |
||||
remoteServiceErrorInfo, |
||||
context.HttpContext.RequestServices, |
||||
statusCodFinder.GetStatusCode(context.HttpContext, context.Exception)); |
||||
|
||||
exceptionWrapHandler.CreateFor(exceptionWrapContext).Wrap(exceptionWrapContext); |
||||
|
||||
context.Result = new ObjectResult(new WrapResult( |
||||
exceptionWrapContext.ErrorInfo.Code, |
||||
exceptionWrapContext.ErrorInfo.Message, |
||||
exceptionWrapContext.ErrorInfo.Details)); |
||||
|
||||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "true"); |
||||
context.HttpContext.Response.StatusCode = (int)wrapOptions.HttpStatusCode; |
||||
} |
||||
|
||||
context.Exception = null; //Handled! |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Logging.Abstractions; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using System; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.AspNetCore.ExceptionHandling; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; |
||||
using Volo.Abp.Authorization; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.ExceptionHandling; |
||||
using Volo.Abp.Http; |
||||
using Volo.Abp.Json; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.ExceptionHandling |
||||
{ |
||||
[Dependency(ReplaceServices = true)] |
||||
[ExposeServices(typeof(AbpExceptionFilter))] |
||||
public class AbpExceptionWrapResultFilter : AbpExceptionFilter, ITransientDependency |
||||
{ |
||||
protected async override Task HandleAndWrapException(ExceptionContext context) |
||||
{ |
||||
var wrapResultChecker = context.GetRequiredService<IWrapResultChecker>(); |
||||
|
||||
if (!wrapResultChecker.WrapOnException(context)) |
||||
{ |
||||
await base.HandleAndWrapException(context); |
||||
return; |
||||
} |
||||
|
||||
//TODO: Trigger an AbpExceptionHandled event or something like that. |
||||
var wrapOptions = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value; |
||||
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>(); |
||||
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, options => |
||||
{ |
||||
options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients; |
||||
options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients; |
||||
}); |
||||
|
||||
var logLevel = context.Exception.GetLogLevel(); |
||||
|
||||
var remoteServiceErrorInfoBuilder = new StringBuilder(); |
||||
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------"); |
||||
remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService<IJsonSerializer>().Serialize(remoteServiceErrorInfo, indented: true)); |
||||
|
||||
var logger = context.GetService<ILogger<AbpExceptionWrapResultFilter>>(NullLogger<AbpExceptionWrapResultFilter>.Instance); |
||||
|
||||
logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString()); |
||||
|
||||
logger.LogException(context.Exception, logLevel); |
||||
|
||||
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception)); |
||||
|
||||
if (context.Exception is AbpAuthorizationException) |
||||
{ |
||||
await context.GetRequiredService<IAbpAuthorizationExceptionHandler>() |
||||
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext); |
||||
} |
||||
else |
||||
{ |
||||
var statusCodFinder = context.GetRequiredService<IHttpExceptionStatusCodeFinder>(); |
||||
var exceptionWrapHandler = context.GetRequiredService<IExceptionWrapHandlerFactory>(); |
||||
|
||||
var exceptionWrapContext = new ExceptionWrapContext( |
||||
context.Exception, |
||||
remoteServiceErrorInfo, |
||||
context.HttpContext.RequestServices, |
||||
statusCodFinder.GetStatusCode(context.HttpContext, context.Exception)); |
||||
|
||||
exceptionWrapHandler.CreateFor(exceptionWrapContext).Wrap(exceptionWrapContext); |
||||
|
||||
context.Result = new ObjectResult(new WrapResult( |
||||
exceptionWrapContext.ErrorInfo.Code, |
||||
exceptionWrapContext.ErrorInfo.Message, |
||||
exceptionWrapContext.ErrorInfo.Details)); |
||||
|
||||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "true"); |
||||
context.HttpContext.Response.StatusCode = (int)wrapOptions.HttpStatusCode; |
||||
} |
||||
|
||||
context.Exception = null; //Handled! |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
using Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
||||
using Sanhe.Abp.Wrapper; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.Options; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.DependencyInjection; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Filters |
||||
{ |
||||
public class AbpWrapResultFilter : IAsyncResultFilter, ITransientDependency |
||||
{ |
||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) |
||||
{ |
||||
if (ShouldWrapResult(context)) |
||||
{ |
||||
await HandleAndWrapResult(context); |
||||
} |
||||
|
||||
await next(); |
||||
} |
||||
|
||||
protected virtual bool ShouldWrapResult(ResultExecutingContext context) |
||||
{ |
||||
var wrapResultChecker = context.GetRequiredService<IWrapResultChecker>(); |
||||
|
||||
return wrapResultChecker.WrapOnExecution(context); |
||||
} |
||||
|
||||
protected virtual Task HandleAndWrapResult(ResultExecutingContext context) |
||||
{ |
||||
var options = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
var actionResultWrapperFactory = context.GetRequiredService<IActionResultWrapperFactory>(); |
||||
actionResultWrapperFactory.CreateFor(context).Wrap(context); |
||||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "true"); |
||||
context.HttpContext.Response.StatusCode = (int)options.HttpStatusCode; |
||||
|
||||
return Task.CompletedTask; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper; |
||||
|
||||
/// <summary> |
||||
/// 包装结果检测器。用于检测是否需要进行包装返回结果。 |
||||
/// </summary> |
||||
public interface IWrapResultChecker |
||||
{ |
||||
bool WrapOnExecution(FilterContext context); |
||||
|
||||
bool WrapOnException(ExceptionContext context); |
||||
|
||||
bool WrapOnException(PageHandlerExecutedContext context); |
||||
} |
@ -0,0 +1,10 @@
|
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Localization |
||||
{ |
||||
[LocalizationResourceName("AbpMvcWrapper")] |
||||
public class AbpMvcWrapperResource |
||||
{ |
||||
|
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
{ |
||||
"culture": "en", |
||||
"texts": { |
||||
"Wrapper:NotFound": "The requested resource was not found on the server." |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
{ |
||||
"culture": "zh-Hans", |
||||
"texts": { |
||||
"Wrapper:NotFound": "在服务器中没有找到请求的资源." |
||||
} |
||||
} |
@ -0,0 +1,176 @@
|
||||
using Microsoft.AspNetCore.Http; |
||||
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
using Microsoft.AspNetCore.Mvc.Controllers; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using System; |
||||
using System.Linq; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Threading; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper; |
||||
|
||||
public class WrapResultChecker : IWrapResultChecker, ISingletonDependency |
||||
{ |
||||
protected AbpWrapperOptions Options { get; } |
||||
|
||||
public WrapResultChecker(IOptionsMonitor<AbpWrapperOptions> optionsMonitor) |
||||
{ |
||||
Options = optionsMonitor.CurrentValue; |
||||
} |
||||
|
||||
public bool WrapOnException(ExceptionContext context) |
||||
{ |
||||
if (!CheckForBase(context)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return CheckForException(context.Exception); |
||||
} |
||||
|
||||
public bool WrapOnException(PageHandlerExecutedContext context) |
||||
{ |
||||
return CheckForException(context.Exception); |
||||
} |
||||
|
||||
public bool WrapOnExecution(FilterContext context) |
||||
{ |
||||
return CheckForBase(context); |
||||
} |
||||
|
||||
protected virtual bool CheckForBase(FilterContext context) |
||||
{ |
||||
if (!Options.IsEnabled) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (context.HttpContext.Request.Headers.ContainsKey(AbpHttpWrapConsts.AbpDontWrapResult)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor descriptor) |
||||
{ |
||||
if (!context.ActionDescriptor.HasObjectResult()) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
//if (!context.HttpContext.Request.CanAccept(MimeTypes.Application.Json)) |
||||
//{ |
||||
// return false; |
||||
//} |
||||
|
||||
if (!CheckForUrl(context)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (!CheckForNamespace(descriptor)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (!CheckForController(descriptor)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (!CheckForInterfaces(descriptor)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (!CheckForMethod(descriptor)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
if (!CheckForReturnType(descriptor)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
protected virtual bool CheckForUrl(FilterContext context) |
||||
{ |
||||
if (!Options.IgnorePrefixUrls.Any()) |
||||
{ |
||||
return true; |
||||
} |
||||
var url = BuildUrl(context.HttpContext); |
||||
return !Options.IgnorePrefixUrls.Any(urlPrefix => urlPrefix.StartsWith(url)); |
||||
} |
||||
|
||||
protected virtual bool CheckForController(ControllerActionDescriptor controllerActionDescriptor) |
||||
{ |
||||
if (controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(IgnoreWrapResultAttribute), true)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return !Options.IgnoreControllers.Any(controller => |
||||
controller.IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo)); |
||||
} |
||||
|
||||
protected virtual bool CheckForMethod(ControllerActionDescriptor controllerActionDescriptor) |
||||
{ |
||||
return !controllerActionDescriptor.MethodInfo.IsDefined(typeof(IgnoreWrapResultAttribute), true); |
||||
} |
||||
|
||||
protected virtual bool CheckForNamespace(ControllerActionDescriptor controllerActionDescriptor) |
||||
{ |
||||
if (string.IsNullOrWhiteSpace(controllerActionDescriptor.ControllerTypeInfo.Namespace)) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
return !Options.IgnoreNamespaces.Any(nsp => |
||||
controllerActionDescriptor.ControllerTypeInfo.Namespace.StartsWith(nsp)); |
||||
} |
||||
|
||||
protected virtual bool CheckForInterfaces(ControllerActionDescriptor controllerActionDescriptor) |
||||
{ |
||||
return !Options.IgnoredInterfaces.Any(type => |
||||
type.IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo)); |
||||
} |
||||
|
||||
protected virtual bool CheckForReturnType(ControllerActionDescriptor controllerActionDescriptor) |
||||
{ |
||||
var returnType = AsyncHelper.UnwrapTask(controllerActionDescriptor.MethodInfo.ReturnType); |
||||
|
||||
if (returnType.IsDefined(typeof(IgnoreWrapResultAttribute), true)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return !Options.IgnoreReturnTypes.Any(type => returnType.IsAssignableFrom(type)); |
||||
} |
||||
|
||||
protected virtual bool CheckForException(Exception exception) |
||||
{ |
||||
return !Options.IgnoreExceptions.Any(ex => ex.IsAssignableFrom(exception.GetType())); |
||||
} |
||||
|
||||
protected virtual string BuildUrl(HttpContext httpContext) |
||||
{ |
||||
//TODO: Add options to include/exclude query, schema and host |
||||
|
||||
var uriBuilder = new UriBuilder(); |
||||
|
||||
uriBuilder.Scheme = httpContext.Request.Scheme; |
||||
uriBuilder.Host = httpContext.Request.Host.Host; |
||||
uriBuilder.Path = httpContext.Request.Path.ToString(); |
||||
uriBuilder.Query = httpContext.Request.QueryString.ToString(); |
||||
|
||||
return uriBuilder.Uri.AbsolutePath; |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Volo.Abp; |
||||
using Volo.Abp.DependencyInjection; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
||||
|
||||
public class ActionResultWrapperFactory : IActionResultWrapperFactory, ITransientDependency |
||||
{ |
||||
public IActionResultWrapper CreateFor(FilterContext context) |
||||
{ |
||||
Check.NotNull(context, nameof(context)); |
||||
|
||||
return context switch |
||||
{ |
||||
ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is ObjectResult => new ObjectActionResultWrapper(), |
||||
ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is JsonResult => new JsonActionResultWrapper(), |
||||
ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is EmptyResult => new EmptyActionResultWrapper(), |
||||
PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is ObjectResult => new ObjectActionResultWrapper(), |
||||
PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is JsonResult => new JsonActionResultWrapper(), |
||||
PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is EmptyResult => new EmptyActionResultWrapper(), |
||||
_ => new NullActionResultWrapper(), |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
||||
|
||||
public class EmptyActionResultWrapper : IActionResultWrapper |
||||
{ |
||||
public void Wrap(FilterContext context) |
||||
{ |
||||
var options = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
switch (context) |
||||
{ |
||||
case ResultExecutingContext resultExecutingContext: |
||||
if (options.ErrorWithEmptyResult) |
||||
{ |
||||
var code = options.CodeWithEmptyResult(context.HttpContext.RequestServices); |
||||
var message = options.MessageWithEmptyResult(context.HttpContext.RequestServices); |
||||
resultExecutingContext.Result = new ObjectResult(new WrapResult(code, message)); |
||||
return; |
||||
} |
||||
resultExecutingContext.Result = new ObjectResult(new WrapResult(options.CodeWithSuccess, result: null)); |
||||
return; |
||||
|
||||
case PageHandlerExecutedContext pageHandlerExecutedContext: |
||||
if (options.ErrorWithEmptyResult) |
||||
{ |
||||
var code = options.CodeWithEmptyResult(context.HttpContext.RequestServices); |
||||
var message = options.MessageWithEmptyResult(context.HttpContext.RequestServices); |
||||
pageHandlerExecutedContext.Result = new ObjectResult(new WrapResult(code, message)); |
||||
return; |
||||
} |
||||
pageHandlerExecutedContext.Result = new ObjectResult(new WrapResult(options.CodeWithSuccess, result: null)); |
||||
return; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
||||
|
||||
/// <summary> |
||||
/// 结果包装器。用于包装返回结果。 |
||||
/// </summary> |
||||
public interface IActionResultWrapper |
||||
{ |
||||
void Wrap(FilterContext context); |
||||
} |
@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
||||
|
||||
public interface IActionResultWrapperFactory |
||||
{ |
||||
IActionResultWrapper CreateFor(FilterContext context); |
||||
} |
@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using System; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping |
||||
{ |
||||
public class JsonActionResultWrapper : IActionResultWrapper |
||||
{ |
||||
public void Wrap(FilterContext context) |
||||
{ |
||||
JsonResult jsonResult = null; |
||||
|
||||
switch (context) |
||||
{ |
||||
case ResultExecutingContext resultExecutingContext: |
||||
jsonResult = resultExecutingContext.Result as JsonResult; |
||||
break; |
||||
|
||||
case PageHandlerExecutedContext pageHandlerExecutedContext: |
||||
jsonResult = pageHandlerExecutedContext.Result as JsonResult; |
||||
break; |
||||
} |
||||
|
||||
if (jsonResult == null) |
||||
{ |
||||
throw new ArgumentException("Action Result should be JsonResult!"); |
||||
} |
||||
|
||||
if (jsonResult.Value is not WrapResult) |
||||
{ |
||||
var options = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
|
||||
jsonResult.Value = new WrapResult(options.CodeWithSuccess, jsonResult.Value); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping |
||||
{ |
||||
public class NullActionResultWrapper : IActionResultWrapper |
||||
{ |
||||
public void Wrap(FilterContext context) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Filters; |
||||
using Microsoft.Extensions.Options; |
||||
using Sanhe.Abp.Wrapper; |
||||
using System; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping |
||||
{ |
||||
public class ObjectActionResultWrapper : IActionResultWrapper |
||||
{ |
||||
public void Wrap(FilterContext context) |
||||
{ |
||||
ObjectResult objectResult = null; |
||||
|
||||
switch (context) |
||||
{ |
||||
case ResultExecutingContext resultExecutingContext: |
||||
objectResult = resultExecutingContext.Result as ObjectResult; |
||||
break; |
||||
|
||||
case PageHandlerExecutedContext pageHandlerExecutedContext: |
||||
objectResult = pageHandlerExecutedContext.Result as ObjectResult; |
||||
break; |
||||
} |
||||
|
||||
if (objectResult == null) |
||||
{ |
||||
throw new ArgumentException("Action Result should be ObjectResult!"); |
||||
} |
||||
|
||||
if (objectResult.Value is not WrapResult) |
||||
{ |
||||
var options = context.GetRequiredService<IOptions<AbpWrapperOptions>>().Value; |
||||
|
||||
if (objectResult.Value == null && options.ErrorWithEmptyResult) |
||||
{ |
||||
var code = options.CodeWithEmptyResult(context.HttpContext.RequestServices); |
||||
var message = options.MessageWithEmptyResult(context.HttpContext.RequestServices); |
||||
objectResult.Value = new WrapResult(code, message); |
||||
} |
||||
else |
||||
{ |
||||
objectResult.Value = new WrapResult(options.CodeWithSuccess, objectResult.Value); |
||||
} |
||||
|
||||
objectResult.DeclaredType = typeof(WrapResult); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue