Browse Source

Wrapper。

master
wwwk 3 years ago committed by wwwk
parent
commit
862ebb3945
  1. 138
      .editorconfig
  2. 5
      Directory.Build.props
  3. 48
      Sanhe.Abp.Startup.sln
  4. 9
      common.props
  5. 9
      configureawait.props
  6. 3
      modules/common/Sanhe.Abp.Wrapper/FodyWeavers.xml
  7. 15
      modules/common/Sanhe.Abp.Wrapper/Sanhe.Abp.Wrapper.csproj
  8. 8
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpHttpWrapConsts.cs
  9. 10
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpWrapperModule.cs
  10. 111
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpWrapperOptions.cs
  11. 40
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/DefaultExceptionWrapHandler.cs
  12. 64
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/ExceptionWrapContext.cs
  13. 31
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/ExceptionWrapHandlerFactory.cs
  14. 13
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IExceptionWrapHandler.cs
  15. 6
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IExceptionWrapHandlerFactory.cs
  16. 9
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IWrapDisabled.cs
  17. 12
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IgnoreWrapResultAttribute.cs
  18. 26
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/WrapResult.cs
  19. 50
      modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/WrapResult`T.cs
  20. 3
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/FodyWeavers.xml
  21. 37
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/README.md
  22. 34
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe.Abp.AspNetCore.Mvc.Wrapper.csproj
  23. 66
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/AbpAspNetCoreMvcWrapperModule.cs
  24. 88
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/ExceptionHandling/AbpExceptionPageWrapResultFilter.cs
  25. 90
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/ExceptionHandling/AbpExceptionWrapResultFilter.cs
  26. 41
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Filters/AbpWrapResultFilter.cs
  27. 15
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/IWrapResultChecker.cs
  28. 10
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/AbpMvcWrapperResource.cs
  29. 6
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources/en.json
  30. 6
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources/zh-Hans.json
  31. 176
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/WrapResultChecker.cs
  32. 25
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/ActionResultWrapperFactory.cs
  33. 39
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/EmptyActionResultWrapper.cs
  34. 11
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/IActionResultWrapper.cs
  35. 8
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/IActionResultWrapperFactory.cs
  36. 40
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/JsonActionResultWrapper.cs
  37. 12
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/NullActionResultWrapper.cs
  38. 51
      modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/ObjectActionResultWrapper.cs

138
.editorconfig

@ -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

5
Directory.Build.props

@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<VoloAbpVersion>5.1.4</VoloAbpVersion>
</PropertyGroup>
</Project>

48
Sanhe.Abp.Startup.sln

@ -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

9
common.props

@ -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>

9
configureawait.props

@ -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>

3
modules/common/Sanhe.Abp.Wrapper/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

15
modules/common/Sanhe.Abp.Wrapper/Sanhe.Abp.Wrapper.csproj

@ -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>

8
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpHttpWrapConsts.cs

@ -0,0 +1,8 @@
namespace Sanhe.Abp.Wrapper;
public class AbpHttpWrapConsts
{
public const string AbpWrapResult = "_AbpWrapResult";
public const string AbpDontWrapResult = "_AbpDontWrapResult";
}

10
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpWrapperModule.cs

@ -0,0 +1,10 @@
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;
namespace Sanhe.Abp.Wrapper;
[DependsOn(typeof(AbpExceptionHandlingModule))]
public class AbpWrapperModule : AbpModule
{
}

111
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/AbpWrapperOptions.cs

@ -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;
}
}

40
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/DefaultExceptionWrapHandler.cs

@ -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);
}
}
}

64
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/ExceptionWrapContext.cs

@ -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;
}
}

31
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/ExceptionWrapHandlerFactory.cs

@ -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;
}
}

13
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IExceptionWrapHandler.cs

@ -0,0 +1,13 @@
namespace Sanhe.Abp.Wrapper;
/// <summary>
/// 异常包装处理器接口。
/// </summary>
public interface IExceptionWrapHandler
{
/// <summary>
/// 包装
/// </summary>
/// <param name="context">异常包装上下文</param>
void Wrap(ExceptionWrapContext context);
}

6
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IExceptionWrapHandlerFactory.cs

@ -0,0 +1,6 @@
namespace Sanhe.Abp.Wrapper;
public interface IExceptionWrapHandlerFactory
{
IExceptionWrapHandler CreateFor(ExceptionWrapContext context);
}

9
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IWrapDisabled.cs

@ -0,0 +1,9 @@
namespace Sanhe.Abp.Wrapper;
/// <summary>
/// 继承此接口表示禁用包装。
/// </summary>
public interface IWrapDisabled
{
}

12
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/IgnoreWrapResultAttribute.cs

@ -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()
{
}
}

26
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/WrapResult.cs

@ -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)
{
}
}

50
modules/common/Sanhe.Abp.Wrapper/Sanhe/Abp/Wrapper/WrapResult`T.cs

@ -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;
}
}

3
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

37
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/README.md

@ -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 指定哪些异常类型不需要处理
## 其他

34
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe.Abp.AspNetCore.Mvc.Wrapper.csproj

@ -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>

66
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/AbpAspNetCoreMvcWrapperModule.cs

@ -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"];
};
});
}
}

88
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/ExceptionHandling/AbpExceptionPageWrapResultFilter.cs

@ -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!
}
}
}

90
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/ExceptionHandling/AbpExceptionWrapResultFilter.cs

@ -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!
}
}
}

41
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Filters/AbpWrapResultFilter.cs

@ -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;
}
}
}

15
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/IWrapResultChecker.cs

@ -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);
}

10
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/AbpMvcWrapperResource.cs

@ -0,0 +1,10 @@
using Volo.Abp.Localization;
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Localization
{
[LocalizationResourceName("AbpMvcWrapper")]
public class AbpMvcWrapperResource
{
}
}

6
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources/en.json

@ -0,0 +1,6 @@
{
"culture": "en",
"texts": {
"Wrapper:NotFound": "The requested resource was not found on the server."
}
}

6
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"Wrapper:NotFound": "在服务器中没有找到请求的资源."
}
}

176
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/WrapResultChecker.cs

@ -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;
}
}

25
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/ActionResultWrapperFactory.cs

@ -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(),
};
}
}

39
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/EmptyActionResultWrapper.cs

@ -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;
}
}
}

11
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/IActionResultWrapper.cs

@ -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);
}

8
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/IActionResultWrapperFactory.cs

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Mvc.Filters;
namespace Sanhe.Abp.AspNetCore.Mvc.Wrapper.Wraping;
public interface IActionResultWrapperFactory
{
IActionResultWrapper CreateFor(FilterContext context);
}

40
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/JsonActionResultWrapper.cs

@ -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);
}
}
}
}

12
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/NullActionResultWrapper.cs

@ -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)
{
}
}
}

51
modules/mvc/Sanhe.Abp.AspNetCore.Mvc.Wrapper/Sanhe/Abp/AspNetCore/Mvc/Wrapper/Wraping/ObjectActionResultWrapper.cs

@ -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…
Cancel
Save