/
ivanstrike
/
tasker
Обзор
Документация
Войти
/
ivanstrike
/
tasker
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Middleware/IdempotencyMiddleware.cs
101 строка
3 KB
ivanstrike
Правка Idempotency conflict
25 окт 2025, 12:34
25 окт 2025, 12:34
261936e
Код
Авторство
О чём код?
using System.Text; using System.Text.Json; using Microsoft.EntityFrameworkCore; using TaskerApi.Data; using TaskerApi.Models; namespace TaskerApi.Middleware; /// <summary> /// Middleware для обеспечения идемпотентности POST запросов /// </summary> public class IdempotencyMiddleware { private readonly RequestDelegate _next; private readonly ILogger<IdempotencyMiddleware> _logger; public IdempotencyMiddleware(RequestDelegate next, ILogger<IdempotencyMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context, ApplicationDbContext dbContext) { if (context.Request.Method != HttpMethod.Post.Method) { await _next(context); return; } if (!context.Request.Headers.TryGetValue("Idempotency-Key", out var idempotencyKey) || string.IsNullOrWhiteSpace(idempotencyKey)) { await _next(context); return; } var key = idempotencyKey.ToString(); var requestPath = context.Request.Path.Value ?? string.Empty; _logger.LogInformation("Processing idempotent request with key: {Key} for path: {Path}", key, requestPath); var existingRequest = await dbContext.IdempotentRequests .FirstOrDefaultAsync(r => r.IdempotencyKey == key && r.RequestPath == requestPath); if (existingRequest != null) { _logger.LogInformation("Found cached response for idempotency key: {Key}", key); await SendIdempotencyErrorResponse(context, idempotencyKey); return; } var originalBodyStream = context.Response.Body; using var responseBody = new MemoryStream(); context.Response.Body = responseBody; await _next(context); context.Response.Body.Seek(0, SeekOrigin.Begin); var responseText = await new StreamReader(context.Response.Body).ReadToEndAsync(); context.Response.Body.Seek(0, SeekOrigin.Begin); if (context.Response.StatusCode >= 200 && context.Response.StatusCode < 300) { var idempotentRequest = new IdempotentRequest { IdempotencyKey = key, RequestPath = requestPath, StatusCode = context.Response.StatusCode, ResponseBody = responseText, CreatedAt = DateTime.UtcNow }; dbContext.IdempotentRequests.Add(idempotentRequest); await dbContext.SaveChangesAsync(); _logger.LogInformation("Saved idempotent request with key: {Key}", key); } await responseBody.CopyToAsync(originalBodyStream); } private async Task SendIdempotencyErrorResponse(HttpContext context, string? idempotencyKey) { context.Response.StatusCode = 409; // Conflict context.Response.ContentType = "application/json"; var response = new { error = "Idempotency conflict", message = $"Request with idempotency key '{idempotencyKey}' has already been processed.", idempotencyKey = idempotencyKey }; await context.Response.WriteAsJsonAsync(response); _logger.LogWarning("Idempotency conflict for key: {Key}", idempotencyKey); } }