Handle API Idempotency in ASP.NET Core

What is API Idempotency?

Idempotency is a crucial concept in API design where making the same request multiple times produces the same result as making it once. This is especially important for operations that modify data (POST, PUT, PATCH, DELETE) where accidental duplicate requests could cause problems like double charges or duplicate records.

Why Implement Idempotency?

Before we dive into code, let’s understand why idempotency matters:

  1. Network reliability: Clients might retry requests if they don’t receive a response
  2. User experience: Users might double-click buttons
  3. Distributed systems: Microservices might need to retry failed calls

Basic Implementation Approach

Here’s a simple way to implement idempotency in ASP.NET Core:

1. Create an Idempotency Filter

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Concurrent;

public class IdempotencyFilter : IActionFilter
{
    private static readonly ConcurrentDictionary<string, bool> _processedRequests = new();

    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Check for Idempotency-Key header
        if (!context.HttpContext.Request.Headers.TryGetValue("Idempotency-Key", out var idempotencyKey))
        {
            context.Result = new BadRequestObjectResult("Idempotency-Key header is required");
            return;
        }

        var key = idempotencyKey.ToString();

        // If we've already processed this key, return conflict
        if (_processedRequests.ContainsKey(key))
        {
            context.Result = new ConflictObjectResult("This request has already been processed");
            return;
        }

        // Mark this key as being processed
        _processedRequests.TryAdd(key, true);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Cleanup if needed
    }
}

2. Register the Filter

In your Program.cs:

builder.Services.AddControllers(options =>
{
    options.Filters.Add<IdempotencyFilter>();
});

3. Using the Idempotent API

POST /api/orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
    "productId": 123,
    "quantity": 2
}

Best Practices

  1. Key generation: Clients should generate unique keys (GUIDs work well)
  2. Key length: Keep it reasonable (128-256 characters max)
  3. Expiration: Set appropriate expiration times (hours/days depending on use case)
  4. HTTP methods: Typically only needed for POST, PUT, PATCH, DELETE
  5. Response caching: Cache successful responses to return identical responses for duplicate requests

Implementing idempotency in your ASP.NET Core APIs helps make them more reliable and resilient to duplicate requests.

Remember that the exact implementation might vary based on your specific requirements, but this gives you a solid foundation to build upon.

Happy Coding 🙂