如何在 ASP.NET Core 3 和 Swashbuckle 5 中为问题详细信息编写 ISchemaFilter?

Muh*_*eed 5 swagger swashbuckle asp.net-core openapi openapi.net

我将 ASP.NET Core 3.0 与 Swashbuckle 5 一起使用。我正在尝试在 ASP.NET Core 3.0 中编写一个ISchemaFilterfor ProblemDetailsProblemDetails返回许多不同的状态代码,例如 400、401、403、406、415、500 等。我想提供一个ProblemDetails取决于状态代码的不同示例。我怎样才能做到这一点?这是我为开始编写的一些代码,只需要填写空白:

public class ProblemDetailsSchemaFilter : ISchemaFilter
{
    private static readonly OpenApiObject Status400ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.1"),
        ["title"] = new OpenApiString("Bad Request"),
        ["status"] = new OpenApiInteger(StatusCodes.Status400BadRequest),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
        ["errors"] = new OpenApiObject()
        {
            ["property1"] = new OpenApiArray()
            {
                new OpenApiString("The property field is required"),
            },
        },
    };

    private static readonly OpenApiObject Status401ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7235#section-3.1"),
        ["title"] = new OpenApiString("Unauthorized"),
        ["status"] = new OpenApiInteger(StatusCodes.Status401Unauthorized),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status403ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.3"),
        ["title"] = new OpenApiString("Forbidden"),
        ["status"] = new OpenApiInteger(StatusCodes.Status403Forbidden),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status404ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.4"),
        ["title"] = new OpenApiString("Not Found"),
        ["status"] = new OpenApiInteger(StatusCodes.Status404NotFound),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status406ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.6"),
        ["title"] = new OpenApiString("Not Acceptable"),
        ["status"] = new OpenApiInteger(StatusCodes.Status406NotAcceptable),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status409ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.8"),
        ["title"] = new OpenApiString("Conflict"),
        ["status"] = new OpenApiInteger(StatusCodes.Status409Conflict),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status415ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.13"),
        ["title"] = new OpenApiString("Unsupported Media Type"),
        ["status"] = new OpenApiInteger(StatusCodes.Status415UnsupportedMediaType),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status422ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc4918#section-11.2"),
        ["title"] = new OpenApiString("Unprocessable Entity"),
        ["status"] = new OpenApiInteger(StatusCodes.Status422UnprocessableEntity),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject Status500ProblemDetails = new OpenApiObject()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.6.1"),
        ["title"] = new OpenApiString("Internal Server Error"),
        ["status"] = new OpenApiInteger(StatusCodes.Status500InternalServerError),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.ApiModel.Type == typeof(ProblemDetails))
        {
            // TODO: Set the default and example based on the status code.
            // schema.Default = ???;
            // schema.Example = ???;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*mps 2

我已采用您的示例并将它们转换为操作过滤器,您可以在其中访问响应。

您可以使用以下操作过滤器:

using System.Collections.Generic;
using System.Net.Mime;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

public class ProblemDetailsOperationFilter : IOperationFilter
{
    private static readonly OpenApiObject _status400ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.1"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status400BadRequest)),
        ["status"] = new OpenApiInteger(StatusCodes.Status400BadRequest),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
        ["errors"] = new OpenApiObject
        {
            ["property1"] = new OpenApiArray
            {
                new OpenApiString("The property field is required"),
            },
        },
    };

    private static readonly OpenApiObject _status401ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7235#section-3.1"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status401Unauthorized)),
        ["status"] = new OpenApiInteger(StatusCodes.Status401Unauthorized),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status403ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.3"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status403Forbidden)),
        ["status"] = new OpenApiInteger(StatusCodes.Status403Forbidden),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status404ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.4"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status404NotFound)),
        ["status"] = new OpenApiInteger(StatusCodes.Status404NotFound),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status406ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.6"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status406NotAcceptable)),
        ["status"] = new OpenApiInteger(StatusCodes.Status406NotAcceptable),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status409ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.8"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status409Conflict)),
        ["status"] = new OpenApiInteger(StatusCodes.Status409Conflict),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status415ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.5.13"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status415UnsupportedMediaType)),
        ["status"] = new OpenApiInteger(StatusCodes.Status415UnsupportedMediaType),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status422ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc4918#section-11.2"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status422UnprocessableEntity)),
        ["status"] = new OpenApiInteger(StatusCodes.Status422UnprocessableEntity),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    private static readonly OpenApiObject _status500ProblemDetails = new()
    {
        ["type"] = new OpenApiString("https://tools.ietf.org/html/rfc7231#section-6.6.1"),
        ["title"] = new OpenApiString(ReasonPhrases.GetReasonPhrase(StatusCodes.Status500InternalServerError)),
        ["status"] = new OpenApiInteger(StatusCodes.Status500InternalServerError),
        ["traceId"] = new OpenApiString("00-982607166a542147b435be3a847ddd71-fc75498eb9f09d48-00"),
    };

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var problemDetails = new Dictionary<string, IOpenApiAny>
        {
            { StatusCodes.Status400BadRequest.ToString(), _status400ProblemDetails },
            { StatusCodes.Status401Unauthorized.ToString(), _status401ProblemDetails },
            { StatusCodes.Status403Forbidden.ToString(), _status403ProblemDetails },
            { StatusCodes.Status404NotFound.ToString(), _status404ProblemDetails },
            { StatusCodes.Status406NotAcceptable.ToString(), _status406ProblemDetails },
            { StatusCodes.Status409Conflict.ToString(), _status409ProblemDetails },
            { StatusCodes.Status415UnsupportedMediaType.ToString(), _status415ProblemDetails },
            { StatusCodes.Status422UnprocessableEntity.ToString(), _status422ProblemDetails },
            { StatusCodes.Status500InternalServerError.ToString(), _status500ProblemDetails },
        };

        foreach (var operationResponse in operation.Responses)
        {
            if (problemDetails.TryGetValue(operationResponse.Key, out var problemDetail))
                operationResponse.Value.Content[MediaTypeNames.Application.Json].Example = problemDetail;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注册它

services.AddSwaggerGen(x =>
{
    ...
    x.OperationFilter<ProblemDetailsOperationFilter>();
    ...
});
Run Code Online (Sandbox Code Playgroud)