6 changed files with 198 additions and 19 deletions
@ -0,0 +1,153 @@ |
|||||
|
using LINGYUN.Abp.Wrapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.ApiExplorer; |
||||
|
using Microsoft.AspNetCore.Mvc.Controllers; |
||||
|
using Microsoft.AspNetCore.Mvc.Formatters; |
||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.ApiExploring; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Reflection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper.ApiExploring; |
||||
|
public class AbpWrapResultApiDescriptionProvider : IApiDescriptionProvider, ITransientDependency |
||||
|
{ |
||||
|
private readonly MvcOptions _mvcOptions; |
||||
|
private readonly AbpWrapperOptions _wrapperOptions; |
||||
|
private readonly AbpRemoteServiceApiDescriptionProviderOptions _providerOptions; |
||||
|
private readonly IWrapResultChecker _wrapResultChecker; |
||||
|
private readonly IModelMetadataProvider _modelMetadataProvider; |
||||
|
|
||||
|
public AbpWrapResultApiDescriptionProvider( |
||||
|
IOptions<MvcOptions> mvcOptions, |
||||
|
IOptions<AbpWrapperOptions> wrapperOptions, |
||||
|
IOptions<AbpRemoteServiceApiDescriptionProviderOptions> providerOptions, |
||||
|
IWrapResultChecker wrapResultChecker, |
||||
|
IModelMetadataProvider modelMetadataProvider) |
||||
|
{ |
||||
|
_mvcOptions = mvcOptions.Value; |
||||
|
_wrapperOptions = wrapperOptions.Value; |
||||
|
_providerOptions = providerOptions.Value; |
||||
|
_wrapResultChecker = wrapResultChecker; |
||||
|
_modelMetadataProvider = modelMetadataProvider; |
||||
|
} |
||||
|
|
||||
|
public int Order => -999; |
||||
|
|
||||
|
public virtual void OnProvidersExecuted(ApiDescriptionProviderContext context) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual void OnProvidersExecuting(ApiDescriptionProviderContext context) |
||||
|
{ |
||||
|
WrapperOKResponse(context); |
||||
|
} |
||||
|
|
||||
|
protected virtual void WrapperOKResponse(ApiDescriptionProviderContext context) |
||||
|
{ |
||||
|
foreach (var result in context.Results.Where(x => x.IsRemoteService())) |
||||
|
{ |
||||
|
var actionProducesResponseTypeAttributes = |
||||
|
ReflectionHelper.GetAttributesOfMemberOrDeclaringType<ProducesResponseTypeAttribute>( |
||||
|
result.ActionDescriptor.GetMethodInfo()); |
||||
|
if (actionProducesResponseTypeAttributes.Any(x => x.StatusCode == (int)_wrapperOptions.HttpStatusCode)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (_wrapResultChecker.WrapOnAction(result.ActionDescriptor) && |
||||
|
result.ActionDescriptor is ControllerActionDescriptor actionDescriptor) |
||||
|
{ |
||||
|
var returnType = AsyncHelper.UnwrapTask(actionDescriptor.MethodInfo.ReturnType); |
||||
|
|
||||
|
Type wrapResultType = null; |
||||
|
if (returnType == null || returnType == typeof(void)) |
||||
|
{ |
||||
|
wrapResultType = typeof(WrapResult); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
wrapResultType = typeof(WrapResult<>).MakeGenericType(returnType); |
||||
|
} |
||||
|
|
||||
|
var responseType = new ApiResponseType |
||||
|
{ |
||||
|
Type = wrapResultType, |
||||
|
StatusCode = (int)_wrapperOptions.HttpStatusCode, |
||||
|
ModelMetadata = _modelMetadataProvider.GetMetadataForType(wrapResultType) |
||||
|
}; |
||||
|
|
||||
|
foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>()) |
||||
|
{ |
||||
|
var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, wrapResultType); |
||||
|
if (formatterSupportedContentTypes == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
foreach (var formatterSupportedContentType in formatterSupportedContentTypes) |
||||
|
{ |
||||
|
responseType.ApiResponseFormats.Add(new ApiResponseFormat |
||||
|
{ |
||||
|
Formatter = (IOutputFormatter)responseTypeMetadataProvider, |
||||
|
MediaType = formatterSupportedContentType |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
// TODO: 是否有必要对其他响应代码定义包装结果?
|
||||
|
// 例外1: 当用户传递 _AbpDontWrapResult 请求头时, 响应结果与预期不一致
|
||||
|
// 例外2: 当控制器Url在被忽略Url中, 响应结果与预期不一致
|
||||
|
// 例外3: 当引发异常在被忽略异常中, 响应结果为 RemoteServiceErrorResponse 对象, 与预期不一致
|
||||
|
|
||||
|
result.SupportedResponseTypes.RemoveAll(x => x.StatusCode == responseType.StatusCode); |
||||
|
result.SupportedResponseTypes.AddIfNotContains( |
||||
|
x => x.StatusCode == responseType.StatusCode, |
||||
|
() => responseType); |
||||
|
WrapperErrorResponse(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void WrapperErrorResponse(ApiDescription description) |
||||
|
{ |
||||
|
foreach (var apiResponse in _providerOptions.SupportedResponseTypes) |
||||
|
{ |
||||
|
var wrapResultType = typeof(WrapResult); |
||||
|
var responseType = new ApiResponseType |
||||
|
{ |
||||
|
Type = wrapResultType, |
||||
|
StatusCode = apiResponse.StatusCode, |
||||
|
ModelMetadata = _modelMetadataProvider.GetMetadataForType(wrapResultType) |
||||
|
}; |
||||
|
|
||||
|
foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>()) |
||||
|
{ |
||||
|
var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, responseType.Type); |
||||
|
if (formatterSupportedContentTypes == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
foreach (var formatterSupportedContentType in formatterSupportedContentTypes) |
||||
|
{ |
||||
|
responseType.ApiResponseFormats.Add(new ApiResponseFormat |
||||
|
{ |
||||
|
Formatter = (IOutputFormatter)responseTypeMetadataProvider, |
||||
|
MediaType = formatterSupportedContentType |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
description.SupportedResponseTypes.RemoveAll(x => x.StatusCode == responseType.StatusCode); |
||||
|
description.SupportedResponseTypes.AddIfNotContains( |
||||
|
x => x.StatusCode == responseType.StatusCode, |
||||
|
() => responseType); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue