9:16 AM Edit This 0 Comments »

Understanding Idempotency in .NET Core APIs: Building Reliable and Duplicate-Safe Applications

 

Introduction

In modern distributed systems, network failures, retries, and user actions such as repeatedly clicking a submit button can result in the same request being sent multiple times. Without proper safeguards, duplicate requests may create duplicate records, process payments multiple times, or generate inconsistent data.

This is where idempotency becomes essential.

In this blog, we'll explore what idempotency is, why it matters, and how to implement it effectively in ASP.NET Core Web APIs.

What is Idempotency?

An operation is idempotent if performing it multiple times produces the same result as performing it once.

Example: POST /api/orders

Consider an API that creates an order:

Without idempotency:

  • First request → Order created
  • Retry request → Another order created

Result: Duplicate orders.

With idempotency:

  • First request → Order created
  • Retry request → Previously created order returned

Result: Only one order exists.

 

 

 

 

Why Do We Need Idempotency?

Idempotency helps solve several real-world challenges:

1. Network Timeouts

A client may not receive a response because of network issues and may resend the request.

2. Client Retries

Mobile applications and frontend applications often automatically retry failed requests.

3. User Errors

Users may click the Submit button multiple times.

4. Distributed Systems

Microservices frequently communicate through asynchronous and potentially unreliable networks.

Without idempotency, these scenarios can cause:

  • Duplicate payments
  • Duplicate orders
  • Duplicate database records
  • Inconsistent business data

HTTP Methods and Idempotency

HTTP specification classifies methods based on idempotent behaviour.

Method

Idempotent

GET

Yes

PUT

Yes

DELETE

Yes

HEAD

Yes

POST

No

PATCH

Depends

 

Implementing Idempotency in ASP.NET Core

The most common approach is using an Idempotency Key.

What is an Idempotency Key?

An idempotency key is a unique identifier generated by the client and sent with every request.

The server stores this key along with the response.

If a request arrives with the same key:

  • Server checks existing records
  • Returns the previously stored response
  • Avoids executing business logic again

 

 

 

 

 

Step 1: Create an Idempotency Entity

public class IdempotencyRecord

{

public Guid Id { get; set; }

 

public string Key { get; set; }

 

public string ResponseBody { get; set; }

 

public int StatusCode { get; set; }

 

public DateTime CreatedAt { get; set; }

}

Step 2: Configure DbContext

public class ApplicationDbContext : DbContext

{

public DbSet<IdempotencyRecord> IdempotencyRecords { get; set; }

 

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)

: base(options)

{

}

}

 

 

 

Step 3: Create Idempotency Filter

public class IdempotencyFilter : IAsyncActionFilter

{

private readonly ApplicationDbContext _dbContext;

 

public IdempotencyFilter(ApplicationDbContext dbContext)

{

_dbContext = dbContext;

}

 

public async Task OnActionExecutionAsync(

ActionExecutingContext context,

ActionExecutionDelegate next)

{

if (!context.HttpContext.Request.Headers.TryGetValue(

"Idempotency-Key",

out var key))

{

await next();

return;

              }

 

var existingRecord = await _dbContext.IdempotencyRecords

.FirstOrDefaultAsync(x => x.Key == key);

 

if (existingRecord != null)

{

context.Result = new ContentResult

{

Content = existingRecord.ResponseBody,

StatusCode = existingRecord.StatusCode,

ContentType = "application/json"

};

 

return;

}

 

var executedContext = await next();

 

if (executedContext.Result is ObjectResult result)

{

var response = JsonSerializer.Serialize(result.Value);

 

_dbContext.IdempotencyRecords.Add(

new IdempotencyRecord

{

Key = key,

ResponseBody = response,

StatusCode = result.StatusCode ?? 200,

CreatedAt = DateTime.UtcNow

});

 

await _dbContext.SaveChangesAsync();

}

}

}

Step 4: Register Filter

builder.Services.AddScoped<IdempotencyFilter>();

Step 5: Apply to Controller

[HttpPost]

[ServiceFilter(typeof(IdempotencyFilter))]

public async Task<IActionResult> CreateOrder(CreateOrderRequest request)

{

var order = new Order

{

Id = Guid.NewGuid(),

Amount = request.Amount

};

 

return Ok(order);

}

 

Common Use Cases

·        Payment Processing

·        Order Creation

·        Ticket Booking

Conclusion

Idempotency is a critical design principle for building resilient and reliable APIs. While HTTP methods like GET, PUT, and DELETE are naturally idempotent, POST operations often require additional mechanisms such as Idempotency Keys to prevent duplicate processing.

In ASP.NET Core, implementing idempotency using middleware, action filters, Redis, or database-backed storage helps ensure that retries, network failures, and user errors do not compromise the integrity of your system.

By adopting idempotency, you can build APIs that are more reliable, scalable, and production-ready—especially in domains like payments, e-commerce, booking platforms, and financial services.

·        

 

 

 

 

 

 

 

 

 

 

 

 

 


0 comments: