Allow ApiControlelrAttribute to be applied to assemblies

Fixes #7343
This commit is contained in:
Pranav K 2018-09-06 11:17:01 -07:00
parent 13281613a5
commit a73d073eea
No known key found for this signature in database
GPG Key ID: 1963DA6D96C3057A
5 changed files with 55 additions and 9 deletions

View File

@ -25,7 +25,8 @@ namespace Microsoft.AspNetCore.Mvc.Api.Analyzers
return false;
}
if (!method.ContainingType.HasAttribute(symbolCache.IApiBehaviorMetadata, inherit: true))
if (!method.ContainingType.HasAttribute(symbolCache.IApiBehaviorMetadata, inherit: true) &&
!method.ContainingAssembly.HasAttribute(symbolCache.IApiBehaviorMetadata))
{
return false;
}

View File

@ -2,16 +2,20 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// Indicates that a type and all derived types are used to serve HTTP API responses. The presence of
/// this attribute can be used to target conventions, filters and other behaviors based on the purpose
/// of the controller.
/// Indicates that a type and all derived types are used to serve HTTP API responses.
/// <para>
/// Controllers decorated with this attribute are configured with features and behavior targeted at improving the
/// developer experience for building APIs.
/// </para>
/// <para>
/// When decorated on an assembly, all controllers in the assembly will be treated as controllers with API behavior.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ApiControllerAttribute : ControllerAttribute, IApiBehaviorMetadata
{
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Internal;
@ -75,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
foreach (var controller in context.Result.Controllers)
{
if (!controller.Attributes.OfType<IApiBehaviorMetadata>().Any())
if (!IsApiController(controller))
{
continue;
}
@ -123,5 +124,17 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
return false;
}
}
private static bool IsApiController(ControllerModel controller)
{
if (controller.Attributes.OfType<IApiBehaviorMetadata>().Any())
{
return true;
}
var controllerAssembly = controller.ControllerType.Assembly;
var assemblyAttributes = controllerAssembly.GetCustomAttributes();
return assemblyAttributes.OfType<IApiBehaviorMetadata>().Any();
}
}
}

View File

@ -4,6 +4,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Api.Analyzers.TestFiles.ApiControllerFactsTest;
using Microsoft.CodeAnalysis;
using Xunit;
@ -110,9 +111,25 @@ namespace TestNamespace
Assert.True(result);
}
private Task<Compilation> GetCompilation()
[Fact]
public async Task IsApiControllerAction_ReturnsTrue_IfAttributeIsDeclaredOnAssembly()
{
var testSource = MvcTestSource.Read(GetType().Name, "TestFile");
// Arrange
var compilation = await GetCompilation(nameof(IsApiControllerAction_ReturnsTrue_IfAttributeIsDeclaredOnAssembly));
var symbolCache = new ApiControllerSymbolCache(compilation);
var type = compilation.GetTypeByMetadataName(typeof(IsApiControllerAction_ReturnsTrue_IfAttributeIsDeclaredOnAssemblyController).FullName);
var method = (IMethodSymbol)type.GetMembers(nameof(IsApiControllerAction_ReturnsTrue_IfAttributeIsDeclaredOnAssemblyController.Action)).First();
// Act
var result = ApiControllerFacts.IsApiControllerAction(symbolCache, method);
// Assert
Assert.True(result);
}
private Task<Compilation> GetCompilation(string testFile = "TestFile")
{
var testSource = MvcTestSource.Read(GetType().Name, testFile);
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
return project.GetCompilationAsync();

View File

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]
namespace Microsoft.AspNetCore.Mvc.Api.Analyzers.TestFiles.ApiControllerFactsTest
{
public class IsApiControllerAction_ReturnsTrue_IfAttributeIsDeclaredOnAssemblyController : ControllerBase
{
public IActionResult Action() => null;
}
}