Skip to content

Commit

Permalink
Merge pull request #3526 from abpframework/maliming/AbpRemoteServiceA…
Browse files Browse the repository at this point in the history
…piDescriptionProvider

Introducing AbpRemoteServiceApiDescriptionProvider
  • Loading branch information
hikalkan authored Apr 12, 2020
2 parents 8f2908b + 8688ba2 commit ab7cf36
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
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.DynamicProxy;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Localization;
Expand Down Expand Up @@ -57,6 +61,25 @@ public override void ConfigureServices(ServiceConfigurationContext context)
options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter));
});

Configure<AbpRemoteServiceApiDescriptionProviderOptions>(options =>
{
var statusCodes = new List<int>
{
(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<AbpAspNetCoreMvcOptions>(options =>
{
if (options.MinifyGeneratedScript == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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.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<MvcOptions> mvcOptionsAccessor,
IOptions<AbpRemoteServiceApiDescriptionProviderOptions> optionsAccessor)
{
_modelMetadataProvider = modelMetadataProvider;
_mvcOptions = mvcOptionsAccessor.Value;
_options = optionsAccessor.Value;
}

public void OnProvidersExecuted(ApiDescriptionProviderContext context)
{
}

/// <summary>
/// The order -999 ensures that this provider is executed right after the
/// Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider.
/// </summary>
public int Order => -999;

public void OnProvidersExecuting(ApiDescriptionProviderContext context)
{
foreach (var apiResponseType in GetApiResponseTypes())
{
foreach (var result in context.Results.Where(x => IsRemoteService(x.ActionDescriptor)))
{
var actionProducesResponseTypeAttributes =
ReflectionHelper.GetAttributesOfMemberOrDeclaringType<ProducesResponseTypeAttribute>(
result.ActionDescriptor.GetMethodInfo());
if (actionProducesResponseTypeAttributes.Any(x => x.StatusCode == apiResponseType.StatusCode))
{
continue;
}

result.SupportedResponseTypes.AddIfNotContains(x => x.StatusCode == apiResponseType.StatusCode,
() => apiResponseType);
}
}
}

protected virtual IEnumerable<ApiResponseType> GetApiResponseTypes()
{
foreach (var apiResponse in _options.SupportedResponseTypes)
{
apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type);

foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>())
{
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 IsRemoteService(ActionDescriptor actionDescriptor)
{
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(actionDescriptor.GetMethodInfo());
return remoteServiceAttr != null && remoteServiceAttr.IsEnabled;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ApiExplorer;

namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
{
public class AbpRemoteServiceApiDescriptionProviderOptions
{
public HashSet<ApiResponseType> SupportedResponseTypes { get; set; } = new HashSet<ApiResponseType>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ public static TAttribute GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<TAtt
?? defaultValue;
}

/// <summary>
/// Tries to gets attributes defined for a class member and it's declaring type including inherited attributes.
/// </summary>
/// <typeparam name="TAttribute">Type of the attribute</typeparam>
/// <param name="memberInfo">MemberInfo</param>
/// <param name="inherit">Inherit attribute from base classes</param>
public static IEnumerable<TAttribute> GetAttributesOfMemberOrDeclaringType<TAttribute>(MemberInfo memberInfo, bool inherit = true)
where TAttribute : class
{
var customAttributes = memberInfo.GetCustomAttributes(true).OfType<TAttribute>();
var declaringTypeCustomAttributes =
memberInfo.DeclaringType?.GetTypeInfo().GetCustomAttributes(true).OfType<TAttribute>();
return declaringTypeCustomAttributes != null
? customAttributes.Concat(declaringTypeCustomAttributes).Distinct()
: customAttributes;
}

/// <summary>
/// Gets value of a property by it's full path from given object
/// </summary>
Expand Down

0 comments on commit ab7cf36

Please sign in to comment.