TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2541 2.63K
День 2100. #ЗаметкиНаПолях
Реализация Идемпотентных POST-запросов в
ASP.NET Core API. Окончание
Начало

Идемпотентность в минимальных API
В минимальных API аналогично реализуем IEndpointFilter:
internal sealed class IdempotencyFilter(
int cacheMins = 60) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext ctx,
EndpointFilterDelegate next)
{
// получаем ключ из запроса
if (!ctx.HttpContext.Request
.Headers.TryGetValue(
"Idempotence-Key",
out var keyValue) ||
!Guid.TryParse(keyValue, out Guid key))
{
return Results.BadRequest(
"Отсутствует заголовок Idempotence-Key");
}

var cache = ctx.HttpContext.RequestServices
.GetRequiredService<IDistributedCache>();

// проверяем, обработан ли ключ
var cacheKey = $"Idempotent_{key}";
var cachedResult = await
cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
var resp = JsonSerializer
.Deserialize<IdempotentResult>(cachedResult)!;
return new IdempotentResult(
resp.StatusCode, resp.Value);
}

object? result = await next(ctx);

// выполняем действие и кэшируем результат
if (result is IStatusCodeHttpResult
{
StatusCode: >= 200 and < 300
} statusResult
and IValueHttpResult valueResult)
{
var status = statusResult.StatusCode
?? StatusCodes.Status200OK;
IdempotentResult resp =
new(status, valueResult.Value);

await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(resp),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(cacheMins)
});
}

return result;
}
}


Создаём свой тип результата:
[method: JsonConstructor]
internal sealed class IdempotentResult(
int status, object? value) : IResult
{
public int StatusCode { get; } = status;
public object? Value { get; } = value;

public Task ExecuteAsync(HttpContext ctx)
{
ctx.Response.StatusCode = StatusCode;
return ctx.Response
.WriteAsJsonAsync(Value);
}
}

Теперь применим фильтр к конечной точке минимальных API:
app.MapPost("/api/orders", CreateOrder)
.RequireAuthorization()
.WithOpenApi()
.AddEndpointFilter<IdempotencyFilter>();


Ещё одним вариантом является реализация логики идемпотентности в промежуточном ПО.

Замечания
1. Длительность кэширования — сложная задача. Разумное время - от нескольких минут до 24–48 часов в зависимости от вашего конкретного варианта использования.
2. Между проверкой и установкой значения кэша существует небольшое окно состояния гонки. Это может быть проблемой, особенно в API с высоким трафиком. Потокобезопасная реализация с использованием распределённой блокировки отлично работает. Она контролирует ситуацию, когда одновременно поступает несколько запросов. Но это должно быть редким явлением.
3. Для распределённого кэша идеально подходит Redis. Кроме того, он обрабатывает распределенную блокировку.
4. Что делать, если клиент повторно использует ключ идемпотентности с другим телом запроса? Вернуть ошибку. Можно хэшировать тело запроса и сохранять его с ключом идемпотентности. Если хеш нового запроса с тем же ключом отличается от сохранённого, возвращается ошибка. Это предотвращает неправильное использование ключей идемпотентности и поддерживает целостность вашего API.

Источник: https://www.milanjovanovic.tech/blog/implementing-idempotent-rest-apis-in-aspnetcore
  • 👍 10
  • 👎 1
More from @netdeveloperdiary
  1. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  2. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  3. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  4. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  5. Oct 3, 2026Post #3360
  6. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →