当操作上存在额外的 ProducesResponseType 属性时,不会继承 ASP.NET Core Web API 约定

问题描述 投票:0回答:1

请参考以下Web API约定以及对应的控制器:

public static class MyAppConventions
{
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound,
                          MediaTypeNames.Application.ProblemJson)]
    [ApiConventionNameMatch(ApiConventionNameMatchBehavior.Prefix)]
    public static void Get()
    { }
}
[ApiConventionType(typeof(MyAppConvention))]
[Route("test")]
public class TestController : ControllerBase
{
    [HttpGet]
    public IActionResult GetData()
    {
        if (condition)
            return NotFound();
        else
            return Ok(new TestDto());
    }
}

问题

如果我添加额外的

ProducesResponseType
来标记相关约定中不存在的自定义响应代码,那么它会忽略约定属性。

[ApiConventionType(typeof(MyAppConvention))]
[Route("test")]
public class TestController : ControllerBase
{
    [ProducesResponseType(typeof(TestDto), StatusCodes.Status200Ok, MediaTypeNames.Application.Json)]
    [HttpGet]
    public IActionResult GetData()
    {
        if (condition)
            return NotFound();
        else
            return Ok(new TestDto());
    }
}

有什么解决方法吗?这与添加到项目中的 OpenAPI 分析器 混淆。

参考文献

  1. https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/conventions?view=aspnetcore-8.0
  2. 自定义 Web API 约定不适用于 ODataController
c# swagger asp.net-core-webapi conventions
1个回答
0
投票

我们遇到了同样的问题,并决定编写自己的 EndpointBuilder 中间件来更改默认行为。

middlewar 将检查您的控制器操作是否包含

ProducesResponseTypeAttribute
ApiConventionResult
属性。如果是这种情况,它会将
ProducesResponseTypeAttribute
从约定复制到端点的元数据中。如果您确实想要覆盖 API 约定中定义的响应类型,您可以添加
IgnoreConventionResponseTypesAttribute

/// <summary>
/// Extension methods for <see cref="IEndpointConventionBuilder"/>.
/// </summary>
public static class IEndpointConventionBuilderExtensions
{

    /// <summary>
    /// By default reponse types defined by <see cref="ApiConventionTypeAttribute"/> are overwritten by <see cref="ProducesResponseTypeAttribute"/> on the action.
    /// To always apply response types from the convention, this method can be used.
    /// </summary>
    /// <remarks>
    /// To disable this behavior on a specific action, apply <see cref="IgnoreConventionResponseTypesAttribute"/> attribute on the action.
    /// </remarks>
    /// <param name="builder">The builder.</param>
    public static void AddApiConventionResponseTypes(this IEndpointConventionBuilder builder)
    {
        builder.Add(builder =>
        {
            var action = builder.Metadata.OfType<ControllerActionDescriptor>().FirstOrDefault();
            if (action is not null)
            {
                if (builder.Metadata.OfType<IgnoreConventionResponseTypesAttribute>().Any()) return;
                var explicitResponseTypes = builder.Metadata.OfType<ProducesResponseTypeAttribute>().ToList();
                if (explicitResponseTypes.Count > 0)
                {
                    if (action?.Properties.TryGetValue(typeof(ApiConventionResult), out var result) == true && result is ApiConventionResult apiConventionResult)
                    {
                        foreach (var response in apiConventionResult.ResponseMetadataProviders)
                        {
                            action.EndpointMetadata.Add(response);
                            action.FilterDescriptors.Add(new FilterDescriptor(response, 30));
                        }
                    }
                }
            }
        });
    }
}

这还允许您在想要覆盖 API 约定中定义的响应类型的控制器操作上添加以下属性

/// <summary>
/// An attribute that specifies that the controller method that this attribute is applied to 
/// should ignore additional response types defined by <see cref="ApiConventionTypeAttribute"/>.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class IgnoreConventionResponseTypesAttribute : Attribute
{
}

您必须将中间件附加到启动时的

MapControllers()
调用

app.MapControllers().AddApiConventionResponseTypes();
© www.soinside.com 2019 - 2024. All rights reserved.