46 changed files with 1607 additions and 2 deletions
@ -0,0 +1,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Wrapper |
|||
{ |
|||
public class AbpWrapperModule: AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Wrapper |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] |
|||
public class IgnoreWrapResultAttribute : Attribute |
|||
{ |
|||
public IgnoreWrapResultAttribute() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Wrapper |
|||
{ |
|||
[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,49 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.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,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\en.json" /> |
|||
<None Remove="LINGYUN\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\AspNetCore\Mvc\Wrapper\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\common\LINGYUN.Abp.Wrapper\LINGYUN.Abp.Wrapper.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,65 @@ |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Filters; |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Localization; |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.ApiExploring; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.AspNetCore.Mvc.ProxyScripting; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.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("/LINGYUN/Abp/AspNetCore/Mvc/Wrapper/Localization/Resources"); |
|||
}); |
|||
|
|||
Configure<MvcOptions>(mvcOptions => |
|||
{ |
|||
// Wrap Result Filter
|
|||
mvcOptions.Filters.AddService(typeof(AbpWrapResultFilter)); |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcWrapperOptions>(options => |
|||
{ |
|||
// 即使重写端点也不包装返回结果
|
|||
// api/abp/api-definition
|
|||
options.IgnoreReturnTypes.Add<ApplicationApiDescriptionModel>(); |
|||
// api/abp/application-configuration
|
|||
options.IgnoreReturnTypes.Add<ApplicationConfigurationDto>(); |
|||
// 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,70 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
{ |
|||
public class AbpAspNetCoreMvcWrapperOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 是否启用包装器
|
|||
/// </summary>
|
|||
public bool IsEnabled { get; set; } |
|||
/// <summary>
|
|||
/// 资源有效时返回代码
|
|||
/// 默认:0
|
|||
/// </summary>
|
|||
public string CodeWithFound { 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; } |
|||
|
|||
public AbpAspNetCoreMvcWrapperOptions() |
|||
{ |
|||
CodeWithFound = "0"; |
|||
HttpStatusCode = HttpStatusCode.OK; |
|||
|
|||
IgnorePrefixUrls = new List<string>(); |
|||
IgnoreNamespaces = new List<string>(); |
|||
|
|||
IgnoreControllers = new TypeList(); |
|||
IgnoreReturnTypes = new TypeList(); |
|||
IgnoreExceptions = new TypeList<Exception>(); |
|||
|
|||
CodeWithEmptyResult = (_) => "404"; |
|||
MessageWithEmptyResult = (_) => "Not Found"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
{ |
|||
public static class AbpHttpWrapConsts |
|||
{ |
|||
public const string AbpWrapResult = "_AbpWrapResult"; |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
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.DependencyInjection; |
|||
using Volo.Abp.ExceptionHandling; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.ExceptionHandling |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(AbpExceptionPageFilter))] |
|||
public class AbpExceptionPageWrapResultFilter: AbpExceptionPageFilter, ITransientDependency |
|||
{ |
|||
protected override bool ShouldHandleException(PageHandlerExecutingContext context) |
|||
{ |
|||
if (!context.ActionDescriptor.CanWarpRsult()) |
|||
{ |
|||
return false; |
|||
} |
|||
return base.ShouldHandleException(context); |
|||
} |
|||
|
|||
protected override async Task HandleAndWrapException(PageHandlerExecutedContext context) |
|||
{ |
|||
var wrapResultChecker = context.GetRequiredService<IWrapResultChecker>(); |
|||
if (!wrapResultChecker.WrapOnException(context)) |
|||
{ |
|||
await base.HandleAndWrapException(context); |
|||
return; |
|||
} |
|||
|
|||
var wrapResultOptions = context.GetRequiredService<IOptions<AbpAspNetCoreMvcWrapperOptions>>().Value; |
|||
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value; |
|||
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>(); |
|||
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients); |
|||
|
|||
var logLevel = context.Exception.GetLogLevel(); |
|||
|
|||
var remoteServiceErrorInfoBuilder = new StringBuilder(); |
|||
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------"); |
|||
remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService<IJsonSerializer>().Serialize(remoteServiceErrorInfo, indented: true)); |
|||
|
|||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "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)); |
|||
|
|||
// Warp Error Response
|
|||
string errorCode = remoteServiceErrorInfo.Code; |
|||
if (context.Exception is IHasErrorCode exceptionWithErrorCode) |
|||
{ |
|||
if (!exceptionWithErrorCode.Code.IsNullOrWhiteSpace() && |
|||
exceptionWithErrorCode.Code.Contains(":")) |
|||
{ |
|||
errorCode = exceptionWithErrorCode.Code.Split(':')[1]; |
|||
} |
|||
else |
|||
{ |
|||
errorCode = exceptionWithErrorCode.Code; |
|||
} |
|||
} |
|||
context.Result = new ObjectResult(new WrapResult(errorCode, remoteServiceErrorInfo.Message, remoteServiceErrorInfo.Details)); |
|||
|
|||
context.Exception = null; //Handled!
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
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 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.DependencyInjection; |
|||
using Volo.Abp.ExceptionHandling; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.ExceptionHandling |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(AbpExceptionFilter))] |
|||
public class AbpExceptionWrapResultFilter : AbpExceptionFilter, ITransientDependency |
|||
{ |
|||
protected override bool ShouldHandleException(ExceptionContext context) |
|||
{ |
|||
var wrapResultChecker = context.GetRequiredService<IWrapResultChecker>(); |
|||
|
|||
if (!wrapResultChecker.WrapOnException(context)) |
|||
{ |
|||
return false; |
|||
} |
|||
return base.ShouldHandleException(context); |
|||
} |
|||
|
|||
protected override async Task HandleAndWrapException(ExceptionContext context) |
|||
{ |
|||
//TODO: Trigger an AbpExceptionHandled event or something like that.
|
|||
var wrapResultOptions = context.GetRequiredService<IOptions<AbpAspNetCoreMvcWrapperOptions>>().Value; |
|||
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value; |
|||
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>(); |
|||
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients); |
|||
|
|||
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)); |
|||
|
|||
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); |
|||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "true"); |
|||
context.HttpContext.Response.StatusCode = (int)wrapResultOptions.HttpStatusCode; |
|||
|
|||
// Warp Error Response
|
|||
string errorCode = remoteServiceErrorInfo.Code; |
|||
if (context.Exception is IHasErrorCode exceptionWithErrorCode) |
|||
{ |
|||
if (!exceptionWithErrorCode.Code.IsNullOrWhiteSpace() && |
|||
exceptionWithErrorCode.Code.Contains(":")) |
|||
{ |
|||
errorCode = exceptionWithErrorCode.Code.Split(':')[1]; |
|||
} |
|||
else |
|||
{ |
|||
errorCode = exceptionWithErrorCode.Code; |
|||
} |
|||
} |
|||
context.Result = new ObjectResult(new WrapResult(errorCode, remoteServiceErrorInfo.Message, remoteServiceErrorInfo.Details)); |
|||
|
|||
context.Exception = null; //Handled!
|
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.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 actionResultWrapperFactory = context.GetRequiredService<IActionResultWrapperFactory>(); |
|||
actionResultWrapperFactory.CreateFor(context).Wrap(context); |
|||
context.HttpContext.Response.Headers.Add(AbpHttpWrapConsts.AbpWrapResult, "true"); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
{ |
|||
public interface IWrapResultChecker |
|||
{ |
|||
bool WrapOnExecution(FilterContext context); |
|||
|
|||
bool WrapOnException(ExceptionContext context); |
|||
|
|||
bool WrapOnException(PageHandlerExecutedContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.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,157 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|||
using Microsoft.AspNetCore.Mvc.Controllers; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
{ |
|||
public class WrapResultChecker : IWrapResultChecker, ISingletonDependency |
|||
{ |
|||
protected AbpAspNetCoreMvcWrapperOptions Options { get; } |
|||
|
|||
public WrapResultChecker(IOptionsMonitor<AbpAspNetCoreMvcWrapperOptions> 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 (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 (!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 CheckForReturnType(ControllerActionDescriptor controllerActionDescriptor) |
|||
{ |
|||
if (controllerActionDescriptor.MethodInfo.ReturnType.IsDefined(typeof(IgnoreWrapResultAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return !Options.IgnoreReturnTypes.Any(type => |
|||
controllerActionDescriptor.MethodInfo.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,38 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping |
|||
{ |
|||
public class ActionResultWrapperFactory : IActionResultWrapperFactory |
|||
{ |
|||
public IActionResultWrapper CreateFor(FilterContext context) |
|||
{ |
|||
Check.NotNull(context, nameof(context)); |
|||
|
|||
switch (context) |
|||
{ |
|||
case ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is ObjectResult: |
|||
return new ObjectActionResultWrapper(); |
|||
|
|||
case ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is JsonResult: |
|||
return new JsonActionResultWrapper(); |
|||
|
|||
case ResultExecutingContext resultExecutingContext when resultExecutingContext.Result is EmptyResult: |
|||
return new EmptyActionResultWrapper(); |
|||
|
|||
case PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is ObjectResult: |
|||
return new ObjectActionResultWrapper(); |
|||
|
|||
case PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is JsonResult: |
|||
return new JsonActionResultWrapper(); |
|||
|
|||
case PageHandlerExecutedContext pageHandlerExecutedContext when pageHandlerExecutedContext.Result is EmptyResult: |
|||
return new EmptyActionResultWrapper(); |
|||
|
|||
default: |
|||
return new NullActionResultWrapper(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping |
|||
{ |
|||
public class EmptyActionResultWrapper : IActionResultWrapper |
|||
{ |
|||
public void Wrap(FilterContext context) |
|||
{ |
|||
var options = context.GetRequiredService<IOptions<AbpAspNetCoreMvcWrapperOptions>>().Value; |
|||
var code = options.CodeWithEmptyResult(context.HttpContext.RequestServices); |
|||
var message = options.MessageWithEmptyResult(context.HttpContext.RequestServices); |
|||
switch (context) |
|||
{ |
|||
case ResultExecutingContext resultExecutingContext: |
|||
resultExecutingContext.Result = new ObjectResult(new WrapResult(code, message)); |
|||
return; |
|||
|
|||
case PageHandlerExecutedContext pageHandlerExecutedContext: |
|||
pageHandlerExecutedContext.Result = new ObjectResult(new WrapResult(code, message)); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping |
|||
{ |
|||
public interface IActionResultWrapper |
|||
{ |
|||
void Wrap(FilterContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping |
|||
{ |
|||
public interface IActionResultWrapperFactory : ITransientDependency |
|||
{ |
|||
IActionResultWrapper CreateFor(FilterContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.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 WrapResult)) |
|||
{ |
|||
var options = context.GetRequiredService<IOptions<AbpAspNetCoreMvcWrapperOptions>>().Value; |
|||
|
|||
jsonResult.Value = new WrapResult(options.CodeWithFound, jsonResult.Value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.Wraping |
|||
{ |
|||
public class NullActionResultWrapper : IActionResultWrapper |
|||
{ |
|||
public void Wrap(FilterContext context) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.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 WrapResult)) |
|||
{ |
|||
var options = context.GetRequiredService<IOptions<AbpAspNetCoreMvcWrapperOptions>>().Value; |
|||
|
|||
if (objectResult.Value != null) |
|||
{ |
|||
objectResult.Value = new WrapResult(options.CodeWithFound, objectResult.Value); |
|||
} |
|||
else |
|||
{ |
|||
var code = options.CodeWithEmptyResult(context.HttpContext.RequestServices); |
|||
var message = options.MessageWithEmptyResult(context.HttpContext.RequestServices); |
|||
objectResult.Value = new WrapResult(code, message); |
|||
} |
|||
|
|||
objectResult.DeclaredType = typeof(WrapResult); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|||
using Microsoft.AspNetCore.Mvc.Controllers; |
|||
|
|||
namespace Microsoft.AspNetCore.Mvc |
|||
{ |
|||
public static class ActionContextExtensions |
|||
{ |
|||
public static bool CanWarpRsult(this ActionDescriptor actionDescriptor) |
|||
{ |
|||
if (actionDescriptor is ControllerActionDescriptor descriptor) |
|||
{ |
|||
if (descriptor.MethodInfo.IsDefined(typeof(IgnoreWrapResultAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (descriptor.ControllerTypeInfo.IsDefined(typeof(IgnoreWrapResultAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
# LINGYUN.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,39 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Content Remove="LINGYUN\Abp\AspNetCore\Mvc\Localization\Resources\en.json" /> |
|||
<Content Remove="LINGYUN\Abp\AspNetCore\Mvc\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\AspNetCore\Mvc\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\AspNetCore\Mvc\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="4.4.0" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="3.0.2"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.*" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\mvc\LINGYUN.Abp.AspNetCore.Mvc.Wrapper\LINGYUN.Abp.AspNetCore.Mvc.Wrapper.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AspNetCore.Tests\LINGYUN.Abp.AspNetCore.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,32 @@ |
|||
using Microsoft.Extensions.Hosting; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc |
|||
{ |
|||
public abstract class AbpAspNetCoreMvcTestBase : AbpAspNetCoreTestBase<Startup> |
|||
{ |
|||
protected override IHostBuilder CreateHostBuilder() |
|||
{ |
|||
return base.CreateHostBuilder(); |
|||
} |
|||
|
|||
private static string CalculateContentRootPath(string projectFileName, string contentPath) |
|||
{ |
|||
var currentDirectory = Directory.GetCurrentDirectory(); |
|||
while (!ContainsFile(currentDirectory, projectFileName)) |
|||
{ |
|||
currentDirectory = new DirectoryInfo(currentDirectory).Parent.FullName; |
|||
} |
|||
|
|||
return Path.Combine(currentDirectory, contentPath); |
|||
} |
|||
|
|||
private static bool ContainsFile(string currentDirectory, string projectFileName) |
|||
{ |
|||
return Directory |
|||
.GetFiles(currentDirectory, "*.*", SearchOption.TopDirectoryOnly) |
|||
.Any(f => Path.GetFileName(f) == projectFileName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
using LINGYUN.Abp.AspNetCore.Mvc.GlobalFeatures; |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Localization; |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Results; |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.ApiExploring; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.AspNetCore.Mvc.Localization; |
|||
using Volo.Abp.AspNetCore.Security.Claims; |
|||
using Volo.Abp.AspNetCore.TestBase; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.GlobalFeatures; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Threading; |
|||
using Volo.Abp.Validation.Localization; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMvcWrapperModule), |
|||
typeof(AbpAspNetCoreTestBaseModule), |
|||
typeof(AbpAspNetCoreMvcModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpAspNetCoreMvcTestModule : AbpModule |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
|||
{ |
|||
options.AddAssemblyResource( |
|||
typeof(MvcTestResource), |
|||
typeof(AbpAspNetCoreMvcTestModule).Assembly |
|||
); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
GlobalFeatureManager.Instance.Modules.GetOrAdd(AbpAspNetCoreMvcTestFeatures.ModuleName, |
|||
() => new AbpAspNetCoreMvcTestFeatures(GlobalFeatureManager.Instance)) |
|||
.EnableAll(); |
|||
}); |
|||
|
|||
context.Services.AddAuthentication(options => |
|||
{ |
|||
options.DefaultChallengeScheme = "Bearer"; |
|||
options.DefaultForbidScheme = "Cookie"; |
|||
}).AddCookie("Cookie").AddJwtBearer("Bearer", _ => { }); |
|||
|
|||
context.Services.AddAuthorization(options => |
|||
{ |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcWrapperOptions>(options => |
|||
{ |
|||
options.IsEnabled = true; |
|||
|
|||
// 测试先清空
|
|||
options.IgnoreControllers.Clear(); |
|||
options.IgnoreExceptions.Clear(); |
|||
options.IgnoreNamespaces.Clear(); |
|||
options.IgnorePrefixUrls.Clear(); |
|||
options.IgnoreReturnTypes.Clear(); |
|||
|
|||
// api/abp/api-definition
|
|||
options.IgnoreControllers.Add<AbpApiDefinitionController>(); |
|||
// api/abp/application-configuration
|
|||
options.IgnoreReturnTypes.Add<ApplicationConfigurationDto>(); |
|||
|
|||
options.IgnorePrefixUrls.Add("/api/dont/wrap-result-test"); |
|||
|
|||
options.IgnoreExceptions.Add<HasDbException>(); |
|||
}); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAspNetCoreMvcTestModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<MvcTestResource>("en") |
|||
.AddBaseTypes( |
|||
typeof(AbpUiResource), |
|||
typeof(AbpValidationResource) |
|||
).AddVirtualJson("/LINGYUN/Abp/AspNetCore/Mvc/Localization/Resources"); |
|||
|
|||
options.Languages.Add(new LanguageInfo("en", "en", "English")); |
|||
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); |
|||
}); |
|||
|
|||
Configure<RazorPagesOptions>(options => |
|||
{ |
|||
options.RootDirectory = "/LINGYUN/Abp/AspNetCore/Mvc"; |
|||
}); |
|||
|
|||
Configure<AbpClaimsMapOptions>(options => |
|||
{ |
|||
|
|||
}); |
|||
|
|||
Configure<AbpExceptionLocalizationOptions>(options => |
|||
{ |
|||
options.ErrorCodeNamespaceMappings.Add("Test", typeof(MvcTestResource)); |
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
app.UseCorrelationId(); |
|||
app.UseStaticFiles(); |
|||
app.UseAbpRequestLocalization(); |
|||
app.UseAbpSecurityHeaders(); |
|||
app.UseRouting(); |
|||
app.UseAbpClaimsMap(); |
|||
app.UseAuthentication(); |
|||
app.UseAuthorization(); |
|||
app.UseAuditing(); |
|||
app.UseUnitOfWork(); |
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.GlobalFeatures; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.GlobalFeatures |
|||
{ |
|||
public class AbpAspNetCoreMvcTestFeatures : GlobalModuleFeatures |
|||
{ |
|||
public const string ModuleName = "AbpAspNetCoreMvcTest"; |
|||
|
|||
public AbpAspNetCoreMvcTestFeatures([NotNull] GlobalFeatureManager featureManager) |
|||
: base(featureManager) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Localization |
|||
{ |
|||
public class MvcTestResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Test:1001": "Test the wrapped exception message." |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Test:1001": "测试包装后的异常消息." |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Results |
|||
{ |
|||
[Route("api/dont/wrap-result-test")] |
|||
public class DontWrapResultController: AbpController |
|||
{ |
|||
[HttpGet] |
|||
public TestResultObject Wrap() |
|||
{ |
|||
return new TestResultObject |
|||
{ |
|||
Id = Guid.NewGuid(), |
|||
DateTime = Clock.Now, |
|||
Double = 3.141592653d, |
|||
Integer = 100, |
|||
Name = "Not Wrap", |
|||
Properties = new Dictionary<string, string> |
|||
{ |
|||
{ "TestKey", "TestValue" } |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Data.Common; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Results |
|||
{ |
|||
public class HasDbException: DbException |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Results |
|||
{ |
|||
public class TestResultObject |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public string Name { get; set; } |
|||
public DateTime DateTime { get; set; } |
|||
public int Integer { get; set; } |
|||
public double Double { get; set; } |
|||
public Dictionary<string , string> Properties { get; set; } |
|||
public TestResultObject() { } |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Localization; |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Data.Common; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Results |
|||
{ |
|||
[Route("api/wrap-result-test")] |
|||
public class WrapResultController: AbpController |
|||
{ |
|||
public WrapResultController() |
|||
{ |
|||
LocalizationResource = typeof(MvcTestResource); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("exception")] |
|||
public TestResultObject WrapBusinessException() |
|||
{ |
|||
throw new BusinessException("Test:1001"); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("wrap")] |
|||
public TestResultObject Wrap() |
|||
{ |
|||
return new TestResultObject |
|||
{ |
|||
Id = Guid.NewGuid(), |
|||
DateTime = Clock.Now, |
|||
Double = 3.141592653d, |
|||
Integer = 100, |
|||
Name = "Wrap", |
|||
Properties = new Dictionary<string, string> |
|||
{ |
|||
{ "TestKey", "TestValue" } |
|||
} |
|||
}; |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("wrap-empty")] |
|||
public TestResultObject WrapEmpty() |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("not-wrap")] |
|||
[IgnoreWrapResult] |
|||
public TestResultObject NotWrap() |
|||
{ |
|||
return new TestResultObject |
|||
{ |
|||
Id = Guid.NewGuid(), |
|||
DateTime = Clock.Now, |
|||
Double = 3.141592653d, |
|||
Integer = 100, |
|||
Name = "Not Wrap", |
|||
Properties = new Dictionary<string, string> |
|||
{ |
|||
{ "TestKey", "TestValue" } |
|||
} |
|||
}; |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("not-wrap-exception")] |
|||
public TestResultObject NotWrapHasDbException() |
|||
{ |
|||
throw new HasDbException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Shouldly; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Http.Modeling; |
|||
using Volo.Abp.Localization; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Results |
|||
{ |
|||
public class WrapResultController_Tests : AbpAspNetCoreMvcTestBase |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Return_Not_Wrap_Result_For_Abp_Api_Definition() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<ApplicationApiDescriptionModel>("/api/abp/api-definition"); |
|||
result.ShouldNotBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Not_Wrap_Result_For_Return_Application_Configuration_Dto() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<ApplicationConfigurationDto>("/api/abp/application-configuration"); |
|||
|
|||
result.ShouldNotBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Not_Wrap_Result_For_Url_Prefix_With_Dont_Wrapper() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<TestResultObject>("/api/dont/wrap-result-test"); |
|||
result.ShouldNotBeNull(); |
|||
result.Name.ShouldBe("Not Wrap"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Wrap_Result_For_Bussiness_Exception() |
|||
{ |
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<WrapResult<TestResultObject>>("/api/wrap-result-test/exception"); |
|||
result.ShouldNotBeNull(); |
|||
result.Code.ShouldBe("1001"); |
|||
result.Message.ShouldBe("测试包装后的异常消息."); |
|||
} |
|||
|
|||
using (CultureHelper.Use("en")) |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<WrapResult<TestResultObject>>("/api/wrap-result-test/exception"); |
|||
result.ShouldNotBeNull(); |
|||
result.Code.ShouldBe("1001"); |
|||
result.Message.ShouldBe("Test the wrapped exception message."); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Not_Wrap_Result_For_Has_Db_Exception() |
|||
{ |
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/wrap-result-test/not-wrap-exception", System.Net.HttpStatusCode.InternalServerError); |
|||
result.ShouldNotBeNull(); |
|||
result.Error.ShouldNotBeNull(); |
|||
result.Error.Message.ShouldBe("对不起,在处理你的请求期间,产生了一个服务器内部错误!"); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Wrap_Result_For_Object_Return_Value() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<WrapResult<TestResultObject>>("/api/wrap-result-test/wrap"); |
|||
result.ShouldNotBeNull(); |
|||
result.Result.Name.ShouldBe("Wrap"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Wrap_Result_For_Empty_Object_Return_Value() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<WrapResult<TestResultObject>>("/api/wrap-result-test/wrap-empty"); |
|||
result.Code.ShouldBe("404"); |
|||
result.Result.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_Not_Wrap_Result_For_Object_Return_Value() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<TestResultObject>("/api/wrap-result-test/not-wrap"); |
|||
result.ShouldNotBeNull(); |
|||
result.Name.ShouldBe("Not Wrap"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AbpAspNetCoreMvcTestModule>(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:53117/", |
|||
"sslPort": 44307 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"LINGYUN.Abp.AspNetCore.Mvc.Tests": { |
|||
"commandName": "Project", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
}, |
|||
"applicationUrl": "https://localhost:5001;http://localhost:5000" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="3.0.2"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="Shouldly" Version="3.0.2" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.TestBase" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,49 @@ |
|||
using Microsoft.Net.Http.Headers; |
|||
using Shouldly; |
|||
using System.Globalization; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.TestBase; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore |
|||
{ |
|||
public class AbpAspNetCoreTestBase : AbpAspNetCoreTestBase<Startup> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public abstract class AbpAspNetCoreTestBase<TStartup> : AbpAspNetCoreIntegratedTestBase<TStartup> |
|||
where TStartup : class |
|||
{ |
|||
protected virtual async Task<T> GetResponseAsObjectAsync<T>(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) |
|||
{ |
|||
var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode); |
|||
return JsonSerializer.Deserialize<T>(strResponse, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) |
|||
{ |
|||
using (var response = await GetResponseAsync(url, expectedStatusCode)) |
|||
{ |
|||
return await response.Content.ReadAsStringAsync(); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<HttpResponseMessage> GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK, bool xmlHttpRequest = false) |
|||
{ |
|||
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, url)) |
|||
{ |
|||
requestMessage.Headers.Add("Accept-Language", CultureInfo.CurrentUICulture.Name); |
|||
if (xmlHttpRequest) |
|||
{ |
|||
requestMessage.Headers.Add(HeaderNames.XRequestedWith, "XMLHttpRequest"); |
|||
} |
|||
var response = await Client.SendAsync(requestMessage); |
|||
response.StatusCode.ShouldBe(expectedStatusCode); |
|||
return response; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore; |
|||
using Volo.Abp.AspNetCore.TestBase; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreTestBaseModule), |
|||
typeof(AbpAspNetCoreModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpAspNetCoreTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAspNetCoreTestModule>(); |
|||
//options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreTestModule>(FindProjectPath(hostingEnvironment));
|
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
app.UseCorrelationId(); |
|||
app.UseStaticFiles(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AbpAspNetCoreTestModule>(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue