Документация¶
Для документирования и тестирования API используем Swagger
- штуку для автоматизированного создания документации WebAPI
на основе стандартной документации C#.
В Swagger
попадают xml
комментарии к публичным методам контроллеров, их аргументам и публичные поля моделей.
Для генерации xml
документации добавим в файл .csproj
<PropertyGroup>
<!-- включение генерации xml файла-->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Отключаем уведомления компилятора, о том что не для всех свойств добавлены комментарии-->
<!-- потому что далеко не везде документирующие комменты нужны-->
<NoWarn>$(NoWarn);1591</NoWarn>
<NoWarn>$(NoWarn);1573</NoWarn>
</PropertyGroup>
Настройка¶
-
Устанавливаем пакеты
Swashbuckle.AspNetCore.SwaggerGen
Swashbuckle.AspNetCore.SwaggerUI
-
Настраиваем в SwaggerUI в
Startup
```c# services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { // название страницы SwaggerUI Title = "Curiosity sample API", // версию API берем из Assembly // выводим чтоб лучше детектить обновления Version = $"v{Assembly.GetExecutingAssembly().GetName().Version}", // любая дополнительная текстовая информация для потребителя API Description = ApiDescriptionBuilder.BuildDescription(), });
// берем документацию из xml файла var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath);
}); ```
-
Для тестирования API очень удобно использовать SwaggerUI. Ниже учим SwaggerUI использовать авторизацию с JWT токеном:
```c# services.AddSwaggerGen(c => { // авторизация в swagger UI c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme() { // Bearer Name = JwtBearerDefaults.AuthenticationScheme, // находится в хедере In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = JwtBearerDefaults.AuthenticationScheme });
var key = new OpenApiSecurityScheme() { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = JwtBearerDefaults.AuthenticationScheme }, In = ParameterLocation.Header }; c.AddSecurityRequirement(new OpenApiSecurityRequirement { {key, new List<string>()} });
}); ```
-
Добавим слой для обслуживания обслуживания созданного документа JSON и пользовательского интерфейса Swagger:
```c# public void Configure(IApplicationBuilder app) { // Enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(c => { // костыль, потому что тут https нормально не определяется // это нужно если хотим тестировать апи из Swagger UI c.PreSerializeFilters.Add((swagger, httpReq) => { var host = httpReq.Host.Value; var scheme = (configuration.Urls ?? "").Contains(host) ? "http" : "https"; swagger.Servers = new List
{new OpenApiServer {Url = $"{scheme}://{host}"}}; }); }); // specifying the Swagger JSON endpoint. app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Curiosity sample API"); c.DocumentTitle = "Curiosity sample API"; });
}
```
Расширение документации¶
Чтобы не создавать разные модели для деталей и обновления объекта мы помечаем поля
только для чтения атрибутом [ReadOnly(true)]
.
Потребитель API будет видеть в POST методе только те поля, которые он может обновлять.
Для того чтоб Swagger
научился скрывать такие поля необходимо ещё в Startup
добавить фильтр:
```c#
services.AddSwaggerGen(c =>
{
c.SchemaFilter<ReadOnlySchemaFilter>();
});
```
```c#
public class ReadOnlySchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema.Properties == null)
{
return;
}
foreach (var schemaProperty in schema.Properties)
{
var property = context.Type.GetProperty(schemaProperty.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property != null)
{
if (property.GetCustomAttributes(typeof(ReadOnlyAttribute), false).SingleOrDefault() is ReadOnlyAttribute attr &&
attr.IsReadOnly)
{
if (schemaProperty.Value.Reference != null)
{
schemaProperty.Value.AllOf = new List<OpenApiSchema>
{
new OpenApiSchema
{
Reference = schemaProperty.Value.Reference
}
};
schemaProperty.Value.Reference = null;
}
schemaProperty.Value.ReadOnly = true;
}
}
}
}
}
```
Для вывода дополнительной информации (например коды ошибок и числовые значения enums) используем класс Startup.ApiDescriptionBuilder.cs
из примера.