不清楚为什么此 Swagger UI 仅针对此特定属性失败并出现“FormatterNotFoundException”异常

Unclear why this Swagger UI fails with 'FormatterNotFoundException' exception only for this specific property

提问人:Beli 提问时间:6/22/2023 更新时间:6/23/2023 访问量:161

问:

如何使示例在 Swagger 中工作,同时在控制器方法中仍然具有 [Produces(“application/json”, “application/xml”)] 定义。

在 .Net Core 6 API 中,已将 Swagger 与 Swashbuckle.AspNetCore.Filters 一起设置,并且可以在 Swagger UI 页中查看请求和响应示例。 为了支持 XML 格式,在程序 .cs 中设置了序列化程序

builder.Services.AddControllers(options =>
{
    options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
    options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});

这在swagger.json生成失败之前效果很好,出现以下错误
Swashbuckle.AspNetCore.Filters.MvcOutputFormatter+FormatterNotFoundException:找不到“application/xml; charset=utf-8”的 OutputFormatter

经分析发现,该问题是由返回特定响应类型的控制器方法之一引起的。该对象不是超级复杂且相对简单的类型,可生成如下所示的 resposne json

{
    "acceptedRequests": [
        {
            "requestId": "7F8092B1-F712-46E7-B991-8D8AEFC3310A",
            "dataAcceptanceStamp": 1687407503,
            "totalDataFilesCount": 3,
            "totalRecordCount": 1674
        }
    ],
    "erroredData": [
        {
            "requestId": "56100665-AF7D-4663-A52E-7D688A5432F6",
            "totalDataFilesCount": 2,
            "receivedFilesCount": 2,
            "processedFilesCount": 1,
            "errors": {
                "statusCode": 422,
                "message": "xyz.dat has multiple validation errors",
                "errors": {
                    "key1": "error desc 1",
                    "key2": "error desc 2"
                }
            }
        }
    ],
    "yetToBeProcessedRequestIds": [
        "G6100665-AF7D-4663-A52E-7D688A5432F6"
    ]
}

下面提供了导致swagger.json失败的控制器方法的定义,问题是由于 SeekApprovalResponse 类型造成的,更具体地说,仅当“Produces”包含“application/xml”时。

[Route("seek/approval")]
[HttpPost]
[Produces("application/json", "application/xml")]
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(SeekApprovalResponse))]
public async Task<IActionResult> SeekRequestApproval([FromBody] SeekApprovalRequest requests)
{
    return Ok(new SeekApprovalResponse());
}

目前尚不清楚是什么让这种“SeekApprovalResponse”类型变得特别,而其他类似的类型工作得很好。

下面提供了此类型的定义以及此类型所需的其他类型

public class SeekApprovalResponse
{
    public ProcessedRequests[] AcceptedRequests { get; set; }

    public ErrorsInRequest[] ErroredData { get; set; }

    public string[] YetToBeProcessedRequestIds { get; set; }
}

{
    public class ProcessedRequests
    {
        public string RequestId { get; set; }
        public long DataAcceptanceStamp { get; set; }
        public byte TotalDataFilesCount { get; set; }
        public uint TotalRecordCount { get; set; }
    }
}

public class ErrorsInRequest
{
    public string RequestId { get; set; }
    public byte TotalDataFilesCount { get; set; }
    public byte ReceivedFilesCount { get; set; }
    public byte ProcessedFilesCount { get; set; }
    public ErrorResponse Errors { get; set; }
}

public class ErrorResponse
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    public Dictionary<string, string> Errors { get; set; }
}

下面提供了 Swagger Example 类(与上面提供的 json 匹配)

public class SeekApprovalResponseExample : IMultipleExamplesProvider<SeekApprovalResponse>
{
    public IEnumerable<SwaggerExample<SeekApprovalResponse>> GetExamples()
    {
        yield return SwaggerExample.Create(
            "Partially approved",
            new SeekApprovalResponse()
            {
                AcceptedRequests = new ProcessedRequests[]
                {
                    new ProcessedRequests
                    {
                        RequestId = "7F8092B1-F712-46E7-B991-8D8AEFC3310A",
                        DataAcceptanceStamp = 1687407503,
                        TotalDataFilesCount = 3,
                        TotalRecordCount = 1674
                    }
                },
                ErroredData = new DataErrorRSDE001[]
                {
                    new DataErrorRSDE001
                    {
                        RequestId = "56100665-AF7D-4663-A52E-7D688A5432F6",
                        TotalDataFilesCount = 2,
                        ReceivedFilesCount = 2,
                        ProcessedFilesCount = 1,
                        Errors = new ErrorResponse
                        {
                            StatusCode = 422,
                            Message = "xyz.dat has multiple validation errors",
                            Errors = new Dictionary<string, string>
                            {
                                {"key1", "error desc 1"},
                                {"key2", "error desc 2"}
                            }
                        }
                    }
                },
                YetToBeProcessedRequestIds = new string[] { "G6100665-AF7D-4663-A52E-7D688A5432F6" }
            }
            );
    }
}

删除“application/xml”后,Swagger UI 将按预期工作,按预期显示 json 示例,表明错误没有误导性。 当“application/xml”被添加回“Produces”时,戳了 SeekApprovalResponse 类型,似乎 ErrorResponse 类中的公共 Dictionary<string, string> 错误给格式化程序带来了麻烦。暂时删除该属性后,将创建swagger.json,并且页面显示正常。如何让 Dictionary<string, string> 属性与 Swagger UI 一起使用?

ASP.NET-CORE-WEBAPI XML 序列化 swagger-ui

评论


答:

0赞 Chen 6/23/2023 #1

您没有启用 XML 格式化程序。请将以下代码行添加到 Program.cs:

builder.Services.AddControllers().AddXmlDataContractSerializerFormatters();

测试结果:enter image description here

评论

0赞 Beli 6/24/2023
谢谢陈!看到您的回复后,试图了解是否有适合不同格式化程序的用例,并在下面遇到
0赞 Beli 6/24/2023
<stackoverflow.com/questions/62514818/......> <stackoverflow.com/questions/58543659/......> 看起来 AddXmlDataContractSerializerFormatters 在两者中更好。但是,从其他 SO 答案中,您能否更新以下答案,如果您觉得它是强大的服务,这可能会对其他人有所帮助。AddControllers(options => 个选项。RespectBrowserAcceptHeader = true)。AddXmlSerializerFormatters()。AddXmlDataContractSerializerFormatters();