我将穷人的解决方案升级为更具描述性的文档,它将在 Swashbuckle 5 中正确显示响应类型。我在 Swagger UI 中获取端点,但 Open API 规范中的描述很笨拙。然后,我将特定的运行状况检查数据类型添加到了 swagger 文档中。我的解决方案是使用自定义响应编写器。
假设您覆盖了响应:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/heartbeat", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
ResponseWriter = HeartbeatUtility.WriteResponse
}) ;
});
假设您有以下健康检查响应作者:
public static class HeartbeatUtility
{
public const string Path = "/heartbeat";
public const string ContentType = "application/json; charset=utf-8";
public const string Status = "status";
public const string TotalTime = "totalTime";
public const string Results = "results";
public const string Name = "Name";
public const string Description = "description";
public const string Data = "data";
public static Task WriteResponse(HttpContext context, HealthReport healthReport)
{
context.Response.ContentType = ContentType;
using (var stream = new MemoryStream())
{
using (var writer = new Utf8JsonWriter(stream, CreateJsonOptions()))
{
writer.WriteStartObject();
writer.WriteString(Status, healthReport.Status.ToString("G"));
writer.WriteString(TotalTime, healthReport.TotalDuration.ToString("c"));
if (healthReport.Entries.Count > 0)
writer.WriteEntries(healthReport.Entries);
writer.WriteEndObject();
}
var json = Encoding.UTF8.GetString(stream.ToArray());
return context.Response.WriteAsync(json);
}
}
private static JsonWriterOptions CreateJsonOptions()
{
return new JsonWriterOptions
{
Indented = true
};
}
private static void WriteEntryData(this Utf8JsonWriter writer, IReadOnlyDictionary<string, object> data)
{
writer.WriteStartObject(Data);
foreach (var item in data)
{
writer.WritePropertyName(item.Key);
var type = item.Value?.GetType() ?? typeof(object);
JsonSerializer.Serialize(writer, item.Value, type);
}
writer.WriteEndObject();
}
private static void WriteEntries(this Utf8JsonWriter writer, IReadOnlyDictionary<string, HealthReportEntry> healthReportEntries)
{
writer.WriteStartArray(Results);
foreach (var entry in healthReportEntries)
{
writer.WriteStartObject();
writer.WriteString(Name, entry.Key);
writer.WriteString(Status, entry.Value.Status.ToString("G"));
if (entry.Value.Description != null)
writer.WriteString(Description, entry.Value.Description);
if (entry.Value.Data.Count > 0)
writer.WriteEntryData(entry.Value.Data);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}
那么你可以有如下的 IDocumentFilter 实现:
public class HealthChecksDocumentFilter : IDocumentFilter
{
private const string _name = "Heartbeat";
private const string _operationId = "GetHeartbeat";
private const string _summary = "Get System Heartbeat";
private const string _description = "Get the heartbeat of the system. If the system is OK, status 200 will be returned, else status 503.";
private const string _okCode = "200";
private const string _okDescription = "Healthy";
private const string _notOkCode = "503";
private const string _notOkDescription = "Not Healthy";
private const string _typeString = "string";
private const string _typeArray = "array";
private const string _typeObject = "object";
private const string _applicationJson = "application/json";
private const string _timespanFormat = "[-][d'.']hh':'mm':'ss['.'fffffff]";
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
ApplyComponentHealthStatus(swaggerDoc);
ApplyComponentHealthReportEntry(swaggerDoc);
ApplyComponentHealthReport(swaggerDoc);
ApplyPathHeartbeat(swaggerDoc);
}
private IList<IOpenApiAny> GetHealthStatusValues()
{
return typeof(HealthStatus)
.GetEnumValues()
.Cast<object>()
.Select(value => (IOpenApiAny)new OpenApiString(value.ToString()))
.ToList();
}
private void ApplyComponentHealthStatus(OpenApiDocument swaggerDoc)
{
swaggerDoc?.Components.Schemas.Add(nameof(HealthStatus), new OpenApiSchema
{
Type = _typeString,
Enum = GetHealthStatusValues()
});
}
private void ApplyComponentHealthReportEntry(OpenApiDocument swaggerDoc)
{
swaggerDoc?.Components.Schemas.Add(nameof(HealthReportEntry), new OpenApiSchema
{
Type = _typeObject,
Properties = new Dictionary<string, OpenApiSchema>
{
{
HeartbeatUtility.Name,
new OpenApiSchema
{
Type = _typeString
}
},
{
HeartbeatUtility.Status,
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = nameof(HealthStatus)
}
}
},
{
HeartbeatUtility.Description,
new OpenApiSchema
{
Type = _typeString,
Nullable = true
}
},
{
HeartbeatUtility.Data,
new OpenApiSchema
{
Type = _typeObject,
Nullable = true,
AdditionalProperties = new OpenApiSchema()
}
}
}
});
}
private void ApplyComponentHealthReport(OpenApiDocument swaggerDoc)
{
swaggerDoc?.Components.Schemas.Add(nameof(HealthReport), new OpenApiSchema()
{
Type = _typeObject,
Properties = new Dictionary<string, OpenApiSchema>
{
{
HeartbeatUtility.Status,
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = nameof(HealthStatus)
}
}
},
{
HeartbeatUtility.TotalTime,
new OpenApiSchema
{
Type = _typeString,
Format = _timespanFormat,
Nullable = true
}
},
{
HeartbeatUtility.Results,
new OpenApiSchema
{
Type = _typeArray,
Nullable = true,
Items = new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = nameof(HealthReportEntry)
}
}
}
}
}
});
}
private void ApplyPathHeartbeat(OpenApiDocument swaggerDoc)
{
swaggerDoc?.Paths.Add(HeartbeatUtility.Path, new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
{
OperationType.Get,
new OpenApiOperation
{
Summary = _summary,
Description = _description,
OperationId = _operationId,
Tags = new List<OpenApiTag>
{
new OpenApiTag
{
Name = _name
}
},
Responses = new OpenApiResponses
{
{
_okCode,
new OpenApiResponse
{
Description = _okDescription,
Content = new Dictionary<string, OpenApiMediaType>
{
{
_applicationJson,
new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = nameof(HealthReport)
}
}
}
}
}
}
},
{
_notOkCode,
new OpenApiResponse
{
Description = _notOkDescription,
Content = new Dictionary<string, OpenApiMediaType>
{
{
_applicationJson,
new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = nameof(HealthReport)
}
}
}
}
}
}
}
}
}
}
}
});
}
}
添加到您的 swaggergen 选项中
options.DocumentFilter<HealthChecksDocumentFilter>();