Setting up for ApiConventionAttribute analyzers (#7912)
* Setting up for ApiConventionAttribute analyzers
This commit is contained in:
parent
e7ab81fe0b
commit
4f7e849cc1
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
internal readonly struct ApiControllerTypeCache
|
||||
{
|
||||
public ApiControllerTypeCache(Compilation compilation)
|
||||
{
|
||||
ApiConventionAttribute = compilation.GetTypeByMetadataName(SymbolNames.ApiConventionAttribute);
|
||||
ProducesResponseTypeAttribute = compilation.GetTypeByMetadataName(SymbolNames.ProducesResponseTypeAttribute);
|
||||
}
|
||||
|
||||
public INamedTypeSymbol ApiConventionAttribute { get; }
|
||||
|
||||
public INamedTypeSymbol ProducesResponseTypeAttribute { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
|
|
@ -31,26 +32,27 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
}
|
||||
|
||||
public static bool HasAttribute(this IMethodSymbol methodSymbol, ITypeSymbol attribute, bool inherit)
|
||||
=> GetAttributes(methodSymbol, attribute, inherit).Any();
|
||||
|
||||
public static IEnumerable<AttributeData> GetAttributes(this IMethodSymbol methodSymbol, ITypeSymbol attribute, bool inherit)
|
||||
{
|
||||
Debug.Assert(methodSymbol != null);
|
||||
Debug.Assert(attribute != null);
|
||||
|
||||
if (!inherit)
|
||||
{
|
||||
return HasAttribute(methodSymbol, attribute);
|
||||
}
|
||||
|
||||
while (methodSymbol != null)
|
||||
{
|
||||
if (methodSymbol.HasAttribute(attribute))
|
||||
foreach (var attributeData in GetAttributes(methodSymbol, attribute))
|
||||
{
|
||||
return true;
|
||||
yield return attributeData;
|
||||
}
|
||||
|
||||
if (!inherit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
methodSymbol = methodSymbol.IsOverride ? methodSymbol.OverriddenMethod : null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasAttribute(this IPropertySymbol propertySymbol, ITypeSymbol attribute, bool inherit)
|
||||
|
|
@ -118,6 +120,17 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<AttributeData> GetAttributes(this ISymbol symbol, ITypeSymbol attribute)
|
||||
{
|
||||
foreach (var declaredAttribute in symbol.GetAttributes())
|
||||
{
|
||||
if (attribute.IsAssignableFrom(declaredAttribute.AttributeClass))
|
||||
{
|
||||
yield return declaredAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ITypeSymbol> GetTypeHierarchy(this ITypeSymbol typeSymbol)
|
||||
{
|
||||
while (typeSymbol != null)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
internal class SymbolApiResponseMetadataProvider
|
||||
{
|
||||
private const string StatusCodeProperty = "StatusCode";
|
||||
private const string StatusCodeConstructorParameter = "statusCode";
|
||||
|
||||
internal static IList<ApiResponseMetadata> GetResponseMetadata(ApiControllerTypeCache typeCache, IMethodSymbol methodSymbol)
|
||||
{
|
||||
var responseMetadataAttributes = methodSymbol.GetAttributes(typeCache.ProducesResponseTypeAttribute, inherit: true);
|
||||
var metadataItems = new List<ApiResponseMetadata>();
|
||||
foreach (var attribute in responseMetadataAttributes)
|
||||
{
|
||||
var statusCode = GetStatusCode(attribute);
|
||||
var metadata = new ApiResponseMetadata(statusCode, attribute, convention: null);
|
||||
|
||||
metadataItems.Add(metadata);
|
||||
}
|
||||
|
||||
return metadataItems;
|
||||
}
|
||||
|
||||
internal static int GetStatusCode(AttributeData attribute)
|
||||
{
|
||||
const int DefaultStatusCode = 200;
|
||||
for (var i = 0; i < attribute.NamedArguments.Length; i++)
|
||||
{
|
||||
var namedArgument = attribute.NamedArguments[i];
|
||||
var namedArgumentValue = namedArgument.Value;
|
||||
if (string.Equals(namedArgument.Key, StatusCodeProperty, StringComparison.Ordinal) &&
|
||||
namedArgumentValue.Kind == TypedConstantKind.Primitive &&
|
||||
(namedArgumentValue.Type.SpecialType & SpecialType.System_Int32) == SpecialType.System_Int32 &&
|
||||
namedArgumentValue.Value is int statusCode)
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.AttributeConstructor == null)
|
||||
{
|
||||
return DefaultStatusCode;
|
||||
}
|
||||
|
||||
var constructorParameters = attribute.AttributeConstructor.Parameters;
|
||||
for (var i = 0; i < constructorParameters.Length; i++)
|
||||
{
|
||||
var parameter = constructorParameters[i];
|
||||
if (string.Equals(parameter.Name, StatusCodeConstructorParameter, StringComparison.Ordinal) &&
|
||||
(parameter.Type.SpecialType & SpecialType.System_Int32) == SpecialType.System_Int32)
|
||||
{
|
||||
if (attribute.ConstructorArguments.Length < i)
|
||||
{
|
||||
return DefaultStatusCode;
|
||||
}
|
||||
|
||||
var argument = attribute.ConstructorArguments[i];
|
||||
if (argument.Kind == TypedConstantKind.Primitive && argument.Value is int statusCode)
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct ApiResponseMetadata
|
||||
{
|
||||
public ApiResponseMetadata(int statusCode, AttributeData attributeData, IMethodSymbol convention)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
Attribute = attributeData;
|
||||
Convention = convention;
|
||||
}
|
||||
|
||||
public int StatusCode { get; }
|
||||
|
||||
public AttributeData Attribute { get; }
|
||||
|
||||
public IMethodSymbol Convention { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
public const string AllowAnonymousAttribute = "Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute";
|
||||
|
||||
public const string ApiConventionAttribute = "Microsoft.AspNetCore.Mvc.ApiConventionAttribute";
|
||||
|
||||
public const string AuthorizeAttribute = "Microsoft.AspNetCore.Authorization.AuthorizeAttribute";
|
||||
|
||||
public const string IFilterMetadataType = "Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata";
|
||||
|
|
@ -15,12 +17,14 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
|
||||
public const string IHtmlHelperType = "Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper";
|
||||
|
||||
public const string IRouteTemplateProvider = "Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider";
|
||||
|
||||
public const string PageModelAttributeType = "Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageModelAttribute";
|
||||
|
||||
public const string PartialMethod = "Partial";
|
||||
|
||||
public const string RenderPartialMethod = "RenderPartial";
|
||||
public const string ProducesResponseTypeAttribute = "Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute";
|
||||
|
||||
public const string IRouteTemplateProvider = "Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider";
|
||||
public const string RenderPartialMethod = "RenderPartial";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -14,8 +15,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns a Accepted (202) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class AcceptedAtActionResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status202Accepted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AcceptedAtActionResult"/> with the values
|
||||
/// provided.
|
||||
|
|
@ -34,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
ActionName = actionName;
|
||||
ControllerName = controllerName;
|
||||
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
|
||||
StatusCode = StatusCodes.Status202Accepted;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -91,4 +95,4 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
context.HttpContext.Response.Headers[HeaderNames.Location] = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,18 @@ using Microsoft.AspNetCore.Routing;
|
|||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns a Accepted (202) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class AcceptedAtRouteResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status202Accepted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AcceptedAtRouteResult"/> class with the values
|
||||
/// provided.
|
||||
|
|
@ -42,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
RouteName = routeName;
|
||||
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
|
||||
StatusCode = StatusCodes.Status202Accepted;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -87,4 +91,4 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
context.HttpContext.Response.Headers[HeaderNames.Location] = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -10,8 +11,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns an Accepted (202) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class AcceptedResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status202Accepted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AcceptedResult"/> class with the values
|
||||
/// provided.
|
||||
|
|
@ -19,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public AcceptedResult()
|
||||
: base(value: null)
|
||||
{
|
||||
StatusCode = StatusCodes.Status202Accepted;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -32,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
: base(value)
|
||||
{
|
||||
Location = location;
|
||||
StatusCode = StatusCodes.Status202Accepted;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -59,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Location = locationUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
|
||||
}
|
||||
|
||||
StatusCode = StatusCodes.Status202Accepted;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -83,4 +87,4 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -10,8 +11,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ObjectResult"/> that when executed will produce a Bad Request (400) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class BadRequestObjectResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status400BadRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="BadRequestObjectResult"/> instance.
|
||||
/// </summary>
|
||||
|
|
@ -19,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public BadRequestObjectResult(object error)
|
||||
: base(error)
|
||||
{
|
||||
StatusCode = StatusCodes.Status400BadRequest;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(modelState));
|
||||
}
|
||||
|
||||
StatusCode = StatusCodes.Status400BadRequest;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,13 +10,16 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// A <see cref="StatusCodeResult"/> that when
|
||||
/// executed will produce a Bad Request (400) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class BadRequestResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status400BadRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="BadRequestResult"/> instance.
|
||||
/// </summary>
|
||||
public BadRequestResult()
|
||||
: base(StatusCodes.Status400BadRequest)
|
||||
: base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -10,8 +11,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ObjectResult"/> that when executed will produce a Conflict (409) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class ConflictObjectResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status409Conflict;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ConflictObjectResult"/> instance.
|
||||
/// </summary>
|
||||
|
|
@ -19,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public ConflictObjectResult(object error)
|
||||
: base(error)
|
||||
{
|
||||
StatusCode = StatusCodes.Status409Conflict;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(modelState));
|
||||
}
|
||||
|
||||
StatusCode = StatusCodes.Status409Conflict;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,23 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="StatusCodeResult"/> that when executed will produce a Conflict (409) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class ConflictResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status409Conflict;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ConflictResult"/> instance.
|
||||
/// </summary>
|
||||
public ConflictResult()
|
||||
: base(StatusCodes.Status409Conflict)
|
||||
: base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,18 @@ using Microsoft.AspNetCore.Routing;
|
|||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class CreatedAtActionResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status201Created;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreatedAtActionResult"/> with the values
|
||||
/// provided.
|
||||
|
|
@ -34,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
ActionName = actionName;
|
||||
ControllerName = controllerName;
|
||||
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
|
||||
StatusCode = StatusCodes.Status201Created;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -8,14 +8,18 @@ using Microsoft.AspNetCore.Routing;
|
|||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class CreatedAtRouteResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status201Created;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreatedAtRouteResult"/> class with the values
|
||||
/// provided.
|
||||
|
|
@ -42,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
RouteName = routeName;
|
||||
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
|
||||
StatusCode = StatusCodes.Status201Created;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -10,8 +11,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class CreatedResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status201Created;
|
||||
|
||||
private string _location;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -29,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
Location = location;
|
||||
StatusCode = StatusCodes.Status201Created;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -55,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Location = location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
|
||||
}
|
||||
|
||||
StatusCode = StatusCodes.Status201Created;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -88,4 +92,4 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
context.HttpContext.Response.Headers[HeaderNames.Location] = Location;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the default status code associated with an <see cref="ActionResult"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute is informational only and does not have any runtime effects.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class DefaultStatusCodeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="DefaultStatusCodeAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="statusCode">The default status code.</param>
|
||||
public DefaultStatusCodeAttribute(int statusCode)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default status code.
|
||||
/// </summary>
|
||||
public int StatusCode { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,23 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="StatusCodeResult"/> that when executed will produce a 204 No Content response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class NoContentResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status204NoContent;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="NoContentResult"/> instance.
|
||||
/// </summary>
|
||||
public NoContentResult()
|
||||
: base(StatusCodes.Status204NoContent)
|
||||
: base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,18 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ObjectResult"/> that when executed will produce a Not Found (404) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class NotFoundObjectResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status404NotFound;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NotFoundObjectResult"/> instance.
|
||||
/// </summary>
|
||||
|
|
@ -17,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public NotFoundObjectResult(object value)
|
||||
: base(value)
|
||||
{
|
||||
StatusCode = StatusCodes.Status404NotFound;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,12 +10,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// Represents an <see cref="StatusCodeResult"/> that when
|
||||
/// executed will produce a Not Found (404) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class NotFoundResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status404NotFound;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NotFoundResult"/> instance.
|
||||
/// </summary>
|
||||
public NotFoundResult() : base(StatusCodes.Status404NotFound)
|
||||
public NotFoundResult() : base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,8 +10,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// An <see cref="ObjectResult"/> that when executed performs content negotiation, formats the entity body, and
|
||||
/// will produce a <see cref="StatusCodes.Status200OK"/> response if negotiation and formatting succeed.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class OkObjectResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status200OK;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OkObjectResult"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -18,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public OkObjectResult(object value)
|
||||
: base(value)
|
||||
{
|
||||
StatusCode = StatusCodes.Status200OK;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,13 +10,16 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// An <see cref="StatusCodeResult"/> that when executed will produce an empty
|
||||
/// <see cref="StatusCodes.Status200OK"/> response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class OkResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status200OK;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OkResult"/> class.
|
||||
/// </summary>
|
||||
public OkResult()
|
||||
: base(StatusCodes.Status200OK)
|
||||
: base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var objectResult = context.Result as ObjectResult;
|
||||
|
||||
if (objectResult != null)
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
// Check if there are any IFormatFilter in the pipeline, and if any of them is active. If there is one,
|
||||
// do not override the content type value.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,12 +10,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// Represents an <see cref="UnauthorizedResult"/> that when
|
||||
/// executed will produce an Unauthorized (401) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class UnauthorizedResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status401Unauthorized;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UnauthorizedResult"/> instance.
|
||||
/// </summary>
|
||||
public UnauthorizedResult() : base(StatusCodes.Status401Unauthorized)
|
||||
public UnauthorizedResult() : base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -9,8 +10,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// An <see cref="ObjectResult"/> that when executed will produce a Unprocessable Entity (422) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class UnprocessableEntityObjectResult : ObjectResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status422UnprocessableEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UnprocessableEntityObjectResult"/> instance.
|
||||
/// </summary>
|
||||
|
|
@ -27,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public UnprocessableEntityObjectResult(object error)
|
||||
: base(error)
|
||||
{
|
||||
StatusCode = StatusCodes.Status422UnprocessableEntity;
|
||||
StatusCode = DefaultStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,13 +10,16 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// A <see cref="StatusCodeResult"/> that when
|
||||
/// executed will produce a Unprocessable Entity (422) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class UnprocessableEntityResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status422UnprocessableEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UnprocessableEntityResult"/> instance.
|
||||
/// </summary>
|
||||
public UnprocessableEntityResult()
|
||||
: base(StatusCodes.Status422UnprocessableEntity)
|
||||
: base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -9,12 +10,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// A <see cref="StatusCodeResult"/> that when
|
||||
/// executed will produce a UnsupportedMediaType (415) response.
|
||||
/// </summary>
|
||||
[DefaultStatusCode(DefaultStatusCode)]
|
||||
public class UnsupportedMediaTypeResult : StatusCodeResult
|
||||
{
|
||||
private const int DefaultStatusCode = StatusCodes.Status415UnsupportedMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="UnsupportedMediaTypeResult"/>.
|
||||
/// </summary>
|
||||
public UnsupportedMediaTypeResult() : base(StatusCodes.Status415UnsupportedMediaType)
|
||||
public UnsupportedMediaTypeResult() : base(DefaultStatusCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,123 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
public class CodeAnalysisExtensionsTest
|
||||
{
|
||||
private static readonly string Namespace = typeof(CodeAnalysisExtensionsTest).Namespace;
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_OnMethodWithoutAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_OnMethodWithoutAttributesClass)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_OnMethodWithoutAttributesClass.Method)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: true);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(attributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_OnNonOverriddenMethod_ReturnsAllAttributesOnCurrentAction()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation("GetAttributes_WithoutMethodOverridding");
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_WithoutMethodOverridding)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_WithoutMethodOverridding.Method)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: true);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
attributes,
|
||||
attributeData => Assert.Equal(201, attributeData.ConstructorArguments[0].Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentAction()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation("GetAttributes_WithMethodOverridding");
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionClass)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionClass.Method)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: false);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
attributes,
|
||||
attributeData => Assert.Equal(400, attributeData.ConstructorArguments[0].Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_WithInheritTrue_ReturnsAllAttributesOnCurrentActionAndOverridingMethod()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation("GetAttributes_WithMethodOverridding");
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionClass)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionClass.Method)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: true);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
attributes,
|
||||
attributeData => Assert.Equal(400, attributeData.ConstructorArguments[0].Value),
|
||||
attributeData => Assert.Equal(200, attributeData.ConstructorArguments[0].Value),
|
||||
attributeData => Assert.Equal(404, attributeData.ConstructorArguments[0].Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_OnNewMethodOfVirtualBaseMethod()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation("GetAttributes_WithNewMethod");
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_WithNewMethodDerived)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_WithNewMethodDerived.VirtualMethod)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: true);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
attributes,
|
||||
attributeData => Assert.Equal(400, attributeData.ConstructorArguments[0].Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_OnNewMethodOfNonVirtualBaseMethod()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation("GetAttributes_WithNewMethod");
|
||||
var attribute = compilation.GetTypeByMetadataName(typeof(ProducesResponseTypeAttribute).FullName);
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetAttributes_WithNewMethodDerived)}");
|
||||
var method = (IMethodSymbol)testClass.GetMembers(nameof(GetAttributes_WithNewMethodDerived.NotVirtualMethod)).First();
|
||||
|
||||
// Act
|
||||
var attributes = CodeAnalysisExtensions.GetAttributes(method, attribute, inherit: true);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
attributes,
|
||||
attributeData => Assert.Equal(401, attributeData.ConstructorArguments[0].Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HasAttribute_ReturnsFalseIfSymbolDoesNotHaveAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttributeTest");
|
||||
var attribute = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttributeTest");
|
||||
var testMethod = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
|
||||
var testProperty = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
|
||||
|
||||
|
|
@ -40,7 +150,7 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.ControllerAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.{nameof(HasAttribute_ReturnsTrueIfTypeHasAttribute)}");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(HasAttribute_ReturnsTrueIfTypeHasAttribute)}");
|
||||
|
||||
// Act
|
||||
var hasAttribute = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: false);
|
||||
|
|
@ -55,7 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.ControllerAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.{nameof(HasAttribute_ReturnsTrueIfBaseTypeHasAttribute)}");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(HasAttribute_ReturnsTrueIfBaseTypeHasAttribute)}");
|
||||
|
||||
// Act
|
||||
var hasAttributeWithoutInherit = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: false);
|
||||
|
|
@ -71,9 +181,9 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var @interface = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IHasAttribute_ReturnsTrueForInterfaceContractOnAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeTest");
|
||||
var derivedClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeDerived");
|
||||
var @interface = compilation.GetTypeByMetadataName($"{Namespace}.IHasAttribute_ReturnsTrueForInterfaceContractOnAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeTest");
|
||||
var derivedClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeDerived");
|
||||
|
||||
// Act
|
||||
var hasAttribute = CodeAnalysisExtensions.HasAttribute(testClass, @interface, inherit: true);
|
||||
|
|
@ -89,8 +199,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsTest");
|
||||
var attribute = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsTest");
|
||||
var method = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
|
||||
|
||||
// Act
|
||||
|
|
@ -105,8 +215,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsTest");
|
||||
var attribute = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsTest");
|
||||
var method = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
|
||||
|
||||
|
||||
|
|
@ -124,8 +234,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnPropertiesAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnProperties");
|
||||
var attribute = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnPropertiesAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnProperties");
|
||||
var property = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
|
||||
|
||||
// Act
|
||||
|
|
@ -140,8 +250,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenProperties");
|
||||
var attribute = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesAttribute");
|
||||
var testClass = compilation.GetTypeByMetadataName($"{Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenProperties");
|
||||
var property = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
|
||||
|
||||
// Act
|
||||
|
|
@ -158,8 +268,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
|
||||
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesB");
|
||||
var source = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
|
||||
var target = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsFalseForDifferentTypesB");
|
||||
|
||||
// Act
|
||||
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
|
||||
|
|
@ -173,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation(nameof(IsAssignable_ReturnsFalseForDifferentTypes));
|
||||
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
|
||||
var source = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
|
||||
var target = compilation.GetTypeByMetadataName($"System.IDisposable");
|
||||
|
||||
// Act
|
||||
|
|
@ -188,8 +298,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
|
||||
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
|
||||
var source = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
|
||||
var target = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
|
||||
|
||||
// Act
|
||||
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
|
||||
|
|
@ -203,8 +313,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterface");
|
||||
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterfaceTest");
|
||||
var source = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterface");
|
||||
var target = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterfaceTest");
|
||||
|
||||
// Act
|
||||
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
|
||||
|
|
@ -220,8 +330,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
{
|
||||
// Arrange
|
||||
var compilation = await GetCompilation();
|
||||
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface");
|
||||
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceTest");
|
||||
var source = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface");
|
||||
var target = compilation.GetTypeByMetadataName($"{Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceTest");
|
||||
|
||||
// Act
|
||||
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
|
|||
[Fact]
|
||||
public Task IsController_ReturnsFalseForValueType() => IsControllerReturnsFalse(typeof(ValueTypeController));
|
||||
|
||||
|
||||
[Fact]
|
||||
public Task IsController_ReturnsFalseForGenericType() => IsControllerReturnsFalse(typeof(OpenGenericController<>));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,311 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Analyzer.Testing;
|
||||
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class SymbolApiResponseMetadataProviderTest
|
||||
{
|
||||
private static readonly string Namespace = typeof(SymbolApiResponseMetadataProviderTest).Namespace;
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsEmptySequence_IfNoAttributesArePresent_ForGetAction()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerWithoutConvention)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerWithoutConvention.GetPerson)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsEmptySequence_IfNoAttributesArePresent_ForPostAction()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerWithoutConvention)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerWithoutConvention.PostPerson)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_IgnoresProducesAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesAttribute)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsValueFromProducesResponseType_WhenStatusCodeIsSpecifiedInConstructor()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeInConstructor)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(201, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsValueFromProducesResponseType_WhenStatusCodeIsSpecifiedInConstructorWithResponseType()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeAndTypeInConstructor)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(202, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsValueFromProducesResponseType_WhenStatusCodeIsSpecifiedInConstructorAndProperty()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeInConstructorAndProperty)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(203, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsValueFromProducesResponseType_WhenStatusCodeAndTypeIsSpecifiedInConstructorAndProperty()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeAndTypeInConstructorAndProperty)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(201, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_ReturnsValueFromCustomProducesResponseType()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithCustomProducesResponseTypeAttributeWithArguments)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(201, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetResponseMetadata_IgnoresCustomResponseTypeMetadataProvider()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithCustomApiResponseMetadataProvider)).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetResponseMetadata_IgnoresAttributesWithIncorrectStatusCodeType()
|
||||
{
|
||||
return GetResponseMetadata_IgnoresInvalidOrUnsupportedAttribues(
|
||||
nameof(GetResponseMetadata_ControllerActionWithAttributes),
|
||||
nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseTypeWithIncorrectStatusCodeType));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetResponseMetadata_IgnoresDerivedAttributesWithoutPropertyOnConstructorArguments()
|
||||
{
|
||||
return GetResponseMetadata_IgnoresInvalidOrUnsupportedAttribues(
|
||||
nameof(GetResponseMetadata_ControllerActionWithAttributes),
|
||||
nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithCustomProducesResponseTypeAttributeWithoutArguments));
|
||||
}
|
||||
|
||||
private async Task GetResponseMetadata_IgnoresInvalidOrUnsupportedAttribues(string typeName, string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{typeName}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(methodName).First();
|
||||
var typeCache = new ApiControllerTypeCache(compilation);
|
||||
|
||||
// Act
|
||||
var result = SymbolApiResponseMetadataProvider.GetResponseMetadata(typeCache, method);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
metadata =>
|
||||
{
|
||||
Assert.Equal(200, metadata.StatusCode);
|
||||
Assert.NotNull(metadata.Attribute);
|
||||
Assert.Null(metadata.Convention);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetStatusCode_ReturnsValueFromConstructor()
|
||||
{
|
||||
// Arrange
|
||||
var actionName = nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeInConstructor);
|
||||
var expected = 201;
|
||||
|
||||
// Act & Assert
|
||||
return GetStatusCodeTest(actionName, expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetStatusCode_ReturnsValueFromProperty()
|
||||
{
|
||||
// Arrange
|
||||
var actionName = nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeAndTypeInConstructorAndProperty);
|
||||
var expected = 201;
|
||||
|
||||
// Act & Assert
|
||||
return GetStatusCodeTest(actionName, expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetStatusCode_ReturnsValueFromConstructor_WhenTypeIsSpecified()
|
||||
{
|
||||
// Arrange
|
||||
var actionName = nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseType_StatusCodeAndTypeInConstructor);
|
||||
var expected = 202;
|
||||
|
||||
// Act & Assert
|
||||
return GetStatusCodeTest(actionName, expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetStatusCode_Returns200_IfTypeIsNotInteger()
|
||||
{
|
||||
// Arrange
|
||||
var actionName = nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithProducesResponseTypeWithIncorrectStatusCodeType);
|
||||
var expected = 200;
|
||||
|
||||
// Act & Assert
|
||||
return GetStatusCodeTest(actionName, expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GetStatusCode_ReturnsValueFromDerivedAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var actionName = nameof(GetResponseMetadata_ControllerActionWithAttributes.ActionWithCustomProducesResponseTypeAttributeWithArguments);
|
||||
var expected = 201;
|
||||
|
||||
// Act & Assert
|
||||
return GetStatusCodeTest(actionName, expected);
|
||||
}
|
||||
|
||||
private async Task GetStatusCodeTest(string actionName, int expected)
|
||||
{
|
||||
var compilation = await GetResponseMetadataCompilation();
|
||||
var controller = compilation.GetTypeByMetadataName($"{Namespace}.{nameof(GetResponseMetadata_ControllerActionWithAttributes)}");
|
||||
var method = (IMethodSymbol)controller.GetMembers(actionName).First();
|
||||
var attribute = method.GetAttributes().First();
|
||||
|
||||
var statusCode = SymbolApiResponseMetadataProvider.GetStatusCode(attribute);
|
||||
|
||||
Assert.Equal(expected, statusCode);
|
||||
}
|
||||
|
||||
private Task<Compilation> GetResponseMetadataCompilation() => GetCompilation("GetResponseMetadataTests");
|
||||
|
||||
private Task<Compilation> GetCompilation(string test)
|
||||
{
|
||||
var testSource = MvcTestSource.Read(GetType().Name, test);
|
||||
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
|
||||
|
||||
return project.GetCompilationAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class GetAttributes_OnMethodWithoutAttributesClass
|
||||
{
|
||||
public void Method() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionBase
|
||||
{
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
public virtual void Method() { }
|
||||
}
|
||||
|
||||
public class GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionClass : GetAttributes_WithInheritFalse_ReturnsAllAttributesOnCurrentActionBase
|
||||
{
|
||||
[ProducesResponseType(400)]
|
||||
public override void Method() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class GetAttributes_WithNewMethodBase
|
||||
{
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
public virtual void VirtualMethod() { }
|
||||
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
public virtual void NotVirtualMethod() { }
|
||||
}
|
||||
|
||||
public class GetAttributes_WithNewMethodDerived : GetAttributes_WithNewMethodBase
|
||||
{
|
||||
[ProducesResponseType(400)]
|
||||
public new void VirtualMethod() { }
|
||||
|
||||
[ProducesResponseType(401)]
|
||||
public new void NotVirtualMethod() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class GetAttributes_WithoutMethodOverridding
|
||||
{
|
||||
[ProducesResponseType(201)]
|
||||
public void Method() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Analyzers
|
||||
{
|
||||
public class GetResponseMetadata_ControllerWithoutConvention : ControllerBase
|
||||
{
|
||||
public ActionResult<Person> GetPerson(int id) => null;
|
||||
|
||||
public ActionResult<Person> PostPerson(Person person) => null;
|
||||
}
|
||||
|
||||
public class GetResponseMetadata_ControllerActionWithAttributes : ControllerBase
|
||||
{
|
||||
[Produces(typeof(Person))]
|
||||
public IActionResult ActionWithProducesAttribute(int id) => null;
|
||||
|
||||
[ProducesResponseType(201)]
|
||||
public IActionResult ActionWithProducesResponseType_StatusCodeInConstructor() => null;
|
||||
|
||||
[ProducesResponseType(typeof(Person), 202)]
|
||||
public IActionResult ActionWithProducesResponseType_StatusCodeAndTypeInConstructor() => null;
|
||||
|
||||
[ProducesResponseType(200, StatusCode = 203)]
|
||||
public IActionResult ActionWithProducesResponseType_StatusCodeInConstructorAndProperty() => null;
|
||||
|
||||
[ProducesResponseType(typeof(object), 200, Type = typeof(Person), StatusCode = 201)]
|
||||
public IActionResult ActionWithProducesResponseType_StatusCodeAndTypeInConstructorAndProperty() => null;
|
||||
|
||||
[CustomResponseType(Type = typeof(Person), StatusCode = 204)]
|
||||
public IActionResult ActionWithCustomApiResponseMetadataProvider() => null;
|
||||
|
||||
[Produces201ResponseType]
|
||||
public IActionResult ActionWithCustomProducesResponseTypeAttributeWithoutArguments() => null;
|
||||
|
||||
[Produces201ResponseType(201)]
|
||||
public IActionResult ActionWithCustomProducesResponseTypeAttributeWithArguments() => null;
|
||||
|
||||
[CustomInvalidProducesResponseType(Type = typeof(Person), StatusCode = "204")]
|
||||
public IActionResult ActionWithProducesResponseTypeWithIncorrectStatusCodeType() => null;
|
||||
}
|
||||
|
||||
public class Person { }
|
||||
|
||||
public class CustomResponseTypeAttribute : Attribute, IApiResponseMetadataProvider
|
||||
{
|
||||
public Type Type { get; set; }
|
||||
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
public void SetContentTypes(MediaTypeCollection contentTypes)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class Produces201ResponseTypeAttribute : ProducesResponseTypeAttribute
|
||||
{
|
||||
public Produces201ResponseTypeAttribute() : base(201) { }
|
||||
|
||||
public Produces201ResponseTypeAttribute(int statusCode) : base(statusCode) { }
|
||||
}
|
||||
|
||||
public class CustomInvalidProducesResponseTypeAttribute : ProducesResponseTypeAttribute
|
||||
{
|
||||
private string _statusCode;
|
||||
|
||||
public CustomInvalidProducesResponseTypeAttribute()
|
||||
: base(0)
|
||||
{
|
||||
}
|
||||
|
||||
public new string StatusCode
|
||||
{
|
||||
get => _statusCode;
|
||||
set
|
||||
{
|
||||
_statusCode = value;
|
||||
base.StatusCode = int.Parse(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue