Я работаю над проектом веб-API ASP.NET Core и пытаюсь фильтровать маршруты, отображаемые в пользовательском интерфейсе Swagger, на основе ключа потребителя, предоставленного в качестве параметра запроса. Я реализовал специальный IDocumentFilter
, который должен удалять все пути из документа Swagger, если потребительский ключ не найден или имеет значение NULL. Однако когда я перехожу по адресу http://localhost:7249/swagger/index.html?consumer=hasad, маршруты не фильтруются должным образом.
Вот соответствующая часть моего кода:
// KeyBasedDocumentFilter.cs
namespace HFCDDM.Api.Filters
{
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
using System.Reflection;
public class KeyBasedDocumentFilter : IDocumentFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<KeyBasedDocumentFilter> _logger;
public KeyBasedDocumentFilter(IHttpContextAccessor httpContextAccessor, ILogger<KeyBasedDocumentFilter> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
_logger.LogInformation("KeyBasedDocumentFilter is being executed.");
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null || !httpContext.Request.Query.TryGetValue("consumer", out var consumerKeys) || consumerKeys.Count == 0)
{
_logger.LogWarning("Consumer key not found in query parameter or is null. No routes will be shown.");
swaggerDoc.Paths.Clear(); // Clear all paths if no valid consumer key is found
return;
}
var consumerKey = consumerKeys.First();
_logger.LogInformation($"Consumer Key: {consumerKey}");
foreach (var path in swaggerDoc.Paths.ToList())
{
var pathItem = path.Value;
var controllerAttributes = context.ApiDescriptions
.Select(apiDesc => apiDesc.ActionDescriptor.EndpointMetadata)
.OfType<ControllerActionDescriptor>()
.SelectMany(descriptor => descriptor.ControllerTypeInfo.GetCustomAttributes<KeyAuthorizeAttribute>())
.ToList();
// If no controller has the KeyAuthorizeAttribute or the consumer key doesn't match, remove the path
if (!controllerAttributes.Any() || !controllerAttributes.Any(attr => attr.Key == consumerKey))
{
swaggerDoc.Paths.Remove(path.Key);
}
}
}
}
}
// Программа.cs
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HFCDDM.Api", Version = "v1" });
c.DocumentFilter<KeyBasedDocumentFilter>();
});
builder.Services.AddScoped<IDocumentFilter, KeyBasedDocumentFilter>();
// Конфигурация промежуточного программного обеспечения
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/swagger/index.html"))
{
var consumerKey = context.Request.Query["consumer"].FirstOrDefault();
if (!string.IsNullOrEmpty(consumerKey))
{
// Pass the consumer key to the document filter
context.Items["ConsumerKey"] = consumerKey;
}
}
await next();
});
// АккаунтКонтроллер.cs
[Route("api/[controller]")]
[ApiController]
[Authorize]
[KeyAuthorize("hasad")]
public class AccountController : ControllerBase
{
// ... (controller actions)
}
// KeyAuthorizeAttribute.cs
namespace HFCDDM.Api
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class KeyAuthorizeAttribute : Attribute
{
public string Key { get; }
public KeyAuthorizeAttribute(string key)
{
Key = key;
}
}
}
KeyBasedDocumentFilter
должен очистить все пути, если потребительский ключ отсутствует или имеет значение NULL, но кажется, что потребительский ключ не распознается правильно. Атрибут KeyAuthorize
применяется к AccountController
, и я ожидаю, что при указании правильного ключа будут показаны только маршруты этого контроллера.
Чего я ожидаю: когда я получаю доступ к пользовательскому интерфейсу Swagger без потребительского ключа или с нулевым ключом, я ожидаю, что маршруты не будут показаны. Когда я указываю правильный ключ потребителя (хасад) в параметре запроса, я ожидаю, что будут видны только маршруты из AccountController
. Однако журналы показывают, что потребительский ключ не найден:
HFCDDM.Api.Filters.KeyBasedDocumentFilter: предупреждение: ключ потребителя не найден в параметре запроса.
Отвечает ли это на ваш вопрос? Как исключить методы из документации Swagger по WebAPI с помощью Swashbuckle
Вот целая рабочая демонстрация, которой вы можете следовать:
public class KeyBasedDocumentFilter : IDocumentFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<KeyBasedDocumentFilter> _logger;
public KeyBasedDocumentFilter(IHttpContextAccessor httpContextAccessor, ILogger<KeyBasedDocumentFilter> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
_logger.LogInformation("KeyBasedDocumentFilter is being executed.");
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null || !httpContext.Request.Query.TryGetValue("consumer", out var consumerKeys) || consumerKeys.Count == 0)
{
_logger.LogWarning("Consumer key not found in query parameter or is null. No routes will be shown.");
swaggerDoc.Paths.Clear(); // Clear all paths if no valid consumer key is found
return;
}
var consumerKey = consumerKeys.First();
_logger.LogInformation($"Consumer Key: {consumerKey}");
var controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(type => typeof(ControllerBase).IsAssignableFrom(type));
// Check each controller type for KeyAuthorizeAttribute
foreach (var controllerType in controllerTypes)
{
var hasKeyAuthorizeAttribute = controllerType.GetCustomAttributes<KeyAuthorizeAttribute>().Any(attr => attr.Key == consumerKey);
if (!hasKeyAuthorizeAttribute)
{
var controllerTypeName = controllerType.Name;
var controllerNameWithoutSuffix = controllerTypeName.EndsWith("Controller") ? controllerTypeName.Substring(0, controllerTypeName.Length - "Controller".Length) : controllerTypeName;
// Remove paths associated with this controller
var pathsToRemove = swaggerDoc.Paths.Keys.Where(path => path.Contains($"/{controllerNameWithoutSuffix}")).ToList();
foreach (var pathToRemove in pathsToRemove)
{
swaggerDoc.Paths.Remove(pathToRemove);
}
}
}
}
}
И нет необходимости использовать конфигурацию промежуточного программного обеспечения, как вы это сделали.
Я использовал JWT для авторизации, я вижу все маршруты, однако, похоже, они просто блокируют их фактическое использование, если у меня нет токена JWT.