diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 21a675ddc4..9a6a224c48 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -11,15 +11,19 @@ using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.Hosting; using Volo.Abp.ApiVersioning; +using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; using Volo.Abp.Http.Modeling; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -50,6 +54,25 @@ namespace Volo.Abp.AspNetCore.Mvc options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter)); }); + Configure(options => + { + var statusCodes = new List + { + (int) HttpStatusCode.Forbidden, + (int) HttpStatusCode.Unauthorized, + (int) HttpStatusCode.BadRequest, + (int) HttpStatusCode.NotFound, + (int) HttpStatusCode.NotImplemented, + (int) HttpStatusCode.InternalServerError + }; + + options.SupportedResponseTypes.AddIfNotContains(statusCodes.Select(statusCode => new ApiResponseType + { + Type = typeof(RemoteServiceErrorResponse), + StatusCode = statusCode + })); + }); + context.Services.PostConfigure(options => { if (options.MinifyGeneratedScript == null) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs new file mode 100644 index 0000000000..1714d74e65 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Linq; +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 Volo.Abp.DependencyInjection; +using Volo.Abp.Reflection; + +namespace Volo.Abp.AspNetCore.Mvc.ApiExploring +{ + public class AbpRemoteServiceApiDescriptionProvider : IApiDescriptionProvider, ITransientDependency + { + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly MvcOptions _mvcOptions; + private readonly AbpRemoteServiceApiDescriptionProviderOptions _options; + + public AbpRemoteServiceApiDescriptionProvider( + IModelMetadataProvider modelMetadataProvider, + IOptions mvcOptionsAccessor, + IOptions optionsAccessor) + { + _modelMetadataProvider = modelMetadataProvider; + _mvcOptions = mvcOptionsAccessor.Value; + _options = optionsAccessor.Value; + } + + public void OnProvidersExecuted(ApiDescriptionProviderContext context) + { + } + + /// + /// The order -999 ensures that this provider is executed right after the + /// Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider. + /// + public int Order => -999; + + public void OnProvidersExecuting(ApiDescriptionProviderContext context) + { + foreach (var apiResponseType in GetApiResponseTypes()) + { + foreach (var result in context.Results.Where(x => ShouldAddResponseTypes(x.ActionDescriptor))) + { + result.SupportedResponseTypes.AddIfNotContains(x => x.StatusCode == apiResponseType.StatusCode, () => apiResponseType); + } + } + } + + protected virtual IEnumerable GetApiResponseTypes() + { + foreach (var apiResponse in _options.SupportedResponseTypes) + { + apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type); + + foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType()) + { + var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, apiResponse.Type); + if (formatterSupportedContentTypes == null) + { + continue; + } + + foreach (var formatterSupportedContentType in formatterSupportedContentTypes) + { + apiResponse.ApiResponseFormats.Add(new ApiResponseFormat + { + Formatter = (IOutputFormatter) responseTypeMetadataProvider, + MediaType = formatterSupportedContentType + }); + } + } + } + + return _options.SupportedResponseTypes; + } + + protected virtual bool ShouldAddResponseTypes(ActionDescriptor actionDescriptor) + { + if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null || + ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null) + { + return false; + } + + var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()); + return remoteServiceAttr != null && remoteServiceAttr.IsEnabled; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs new file mode 100644 index 0000000000..74e631c53c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ApiExplorer; + +namespace Volo.Abp.AspNetCore.Mvc.ApiExploring +{ + public class AbpRemoteServiceApiDescriptionProviderOptions + { + public HashSet SupportedResponseTypes { get; set; } = new HashSet(); + } +} \ No newline at end of file