相同状态代码200OK的多个ProducesResponseType

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

我正在使用 swagger ui,我的控制器中有这样的操作:

[HttpGet("{id}")]
[AllowAnonymous]
[ProducesResponseType(typeof(PublicDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PrivateDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUser(string id)
{
    try
    {

        ...

        if (...) 
        {
            var dto = _mapper.Map<PrivateDto>(user);
            return Ok(dto);
        }
        else
        {
            var dto = _mapper.Map<PublicDto>(user);
            return Ok(dto);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error inside action");
        return StatusCode(500, "Internal server error");
    }
}

问题是,在我的 swagger UI 页面上,我只能在“响应”区域中看到其中之一,并且只获得其中一个的方案。有没有一种方法可以让多个对象响应一个状态码?

c# asp.net-core swagger swagger-ui swashbuckle.aspnetcore
2个回答
1
投票

这不是一个错误,而是 SB 所基于的开放 API 规范的限制。具体来说,每个响应代码只能有一个响应对象。请参阅https://spec.openapis.org/oas/v3.1.0#responses-object


0
投票

我使用 https://github.com/mcintyre321/OneOf 来实现此目的。

ClientFilePatchResult.cs(响应模型)

[GenerateOneOf]
public partial class ClientFilePatchResult
    : OneOfBase<
        ClientUpdateResult,
        DivisionUpdateResult,
        LinkedPartyUpdateResult,
        ContactUpdateResult,
        None>
{
}

ClientFileController.cs

    [HttpPatch]
    [Route("{id}")]
    [ProducesResponseType(typeof(ClientFilePatchResult), StatusCodes.Status200OK)]
    public async Task<IActionResult> UpdateField([FromRoute] int id, [FromBody] ClientFilePatchVm model)
    {
        ...
        var result = await _jobService.PatchAsync(id, sm);
        return result.Value is None ? Ok() : Ok(result.Value);
    }

OneOfOperationFilter.cs(请参阅 https://github.com/domaindrivendev/Swashbuckle.AspNetCore#operation-filters

public class OneOfOperationFilter : IOperationFilter
{
    private static IReadOnlyList<Type> GetGenericParameterTypes(Type type)
    {
        if (type.IsGenericType && type.IsAssignableTo(typeof(IOneOf)))
        {
            return type.GetGenericArguments().ToList();
        }
        else
        {
            throw new ArgumentException($"Type {type.FullName} is not assignable to {typeof(IOneOf)}");
        }
    }

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var oneOfResponseAttributes = context.MethodInfo
            .GetCustomAttributes()
            .OfType<ProducesResponseTypeAttribute>()
            .ToList()
            .AsReadOnly();

        if (!oneOfResponseAttributes.Any())
            return;

        // process all ProducesResponseType attributes
        foreach (var oneOfResponseAttribute in oneOfResponseAttributes)
        {
            var statusCode = oneOfResponseAttribute.StatusCode;
            var oneOfResponseType = oneOfResponseAttribute.Type;

            // skip if current type is not IOneOf
            if (!oneOfResponseType.IsAssignableTo(typeof(IOneOf)))
                continue;

            // find parent class of type OneOf<T1, ...> or OneOfBase<T1, ...>
            while (oneOfResponseType is { IsGenericType: false })
            {
                oneOfResponseType = oneOfResponseType.BaseType;
            }

            // skip if unable to find appropriate parent class
            if (oneOfResponseType == null)
                continue;

            var allOneOfTypes = GetGenericParameterTypes(oneOfResponseType);

            var oneOfSchemas = new List<OpenApiSchema>();

            foreach (var oneOfType in allOneOfTypes)
            {
                var oneOfTypeSchema = context.SchemaGenerator.GenerateSchema(oneOfType, context.SchemaRepository);
                if (oneOfTypeSchema != null)
                {
                    oneOfSchemas.Add(oneOfTypeSchema);
                }
            }

            if (oneOfSchemas.Any())
            {
                var operationResponse = operation.Responses[statusCode.ToStringInvariant()];
                operationResponse.Content.Clear();
                operationResponse.Content["application/json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchema
                    {
                        Type = "object",
                        OneOf = oneOfSchemas,
                    },
                };
            }
        }
    }
}

因此,我可以在我的 swagger UI 中看到适当的模式:

          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ClientUpdateResult"
                    },
                    {
                      "$ref": "#/components/schemas/DivisionUpdateResult"
                    },
                    {
                      "$ref": "#/components/schemas/LinkedPartyUpdateResult"
                    },
                    {
                      "$ref": "#/components/schemas/ContactUpdateResult"
                    },
                    {
                      "$ref": "#/components/schemas/None"
                    }
                  ]
                }
              }
            }
          }

© www.soinside.com 2019 - 2024. All rights reserved.