Categories
ASP.Net Core

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 🙂

Categories
ASP.Net Core

Getting logged in user ID in non-controller class in ASP.Net Core

It’s easy to retrieve the current logged-in user’s information, such as user ID, username, or email, in an ASP.Net Core controller class. However, it can be tricky to access this information in other classes, like the IdentityDbContext class. Here’s an example of how to extract the current user in a non-controller class, specifically in the IdentityDbContext class:

var httpContextAccessor = this.GetService<IHttpContextAccessor>();
var userId = httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;

This example demonstrates how to retrieve the user ID. Hopefully, you find it helpful.

Happy coding 🙂

Categories
ASP.Net Core

Extract year from database date in C#

In one of my projects, I was needed to extract a year from a database query and use it at a later stage in the program. I had to declare a nullable Datetime variable and assign the value from the database in a loop.

DateTime? expiryDate = DateTime.Now;

Database query result need to assign in expiryDate variable

foreach (dynamic item in data)
            {                
                expiryDate = DateTime.ParseExact(item.ExpiryDate.ToString(),
                                                "dd/MM/yyyy HH:mm:ss",
                                                CultureInfo.InvariantCulture,
                                                DateTimeStyles.AdjustToUniversal);
            }

The date format in the database was like this “2021-06-30 00:00:00”, which required to convert to “dd/MM/yyyy” format.

int year = expiryDate.Value.Year

The above line of code will output the year from the expiryDate variable. We can get day and month from this variable in the same way.

Happy Coding 🙂

Categories
ASP.Net 5 ASP.Net Core

Hot Reloading in ASP.NET core apps

During the development we often need to make small changes in JavaScript, in CSS or in HTML, to check the change on the browse, we have to compile and run the application in Visual Studio, if app is already running in the browser, then have to stop it and restart the project from VS. I found it quite inconvenient and not productive. Whereas other front end tools like ReactJs, Vue has hot reloading feature with their integrated CLI, as soon as we make changes in the code browser immediately reloads that change.

Now .NET Core has this feature with one simple command

dotnet watch run

use this command from project folder using a cmd prompt, this command will start the app in the browser and will watch for changes in the files and restarts when a change is detected. This feature made development productive and painless.

Happy Coding 🙂

Categories
ASP.Net Core Identity

Get logged in user name in Core 3.0

To get current logged in user from Identity 2 in .net core 3.0 and 3.1, use the following code.

User.FindFirst(ClaimTypes.Name).Value

Happy Coding 🙂

Categories
ASP.Net Core Identity

ASP.Net Core Seed User and Role

Data seeding for Identity user tables are different. Here is an example code how to seed data without extending the Identity User table. First step to create a class in Data folder Name it “ApplicationDbInitializer.cs”, following code will be in this class.

using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AppName.Data
{
    public class ApplicationDbInitializer
    {
        public static void SeedUsers(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            string admin = "Admin";
            string normalUser = "NormalUser";
            string password = "p@55w0rD"; 

            if (roleManager.FindByNameAsync("Admin").Result == null)
            {
                IdentityRole role = new IdentityRole
                {
                    Name = admin,
                    NormalizedName = "ADMIN"
                };

                roleManager.CreateAsync(role).Wait();
            }

            if (roleManager.FindByNameAsync("NormalUser").Result == null)
            {
                IdentityRole role1 = new IdentityRole
                {
                    Name = normalUser,
                    NormalizedName = "NORMALUSER"
                };

                roleManager.CreateAsync(role1).Wait();                
            }

            if (userManager.FindByEmailAsync("admin@domain.com").Result == null)
            {
                IdentityUser user = new IdentityUser
                {
                    UserName = "admin@domain.com",
                    Email = "admin@domain.com"
                };

                IdentityResult result = userManager.CreateAsync(user, password).Result;

                if (result.Succeeded)
                {
                    userManager.AddToRoleAsync(user, admin).Wait();
                }
            }
        }
    }
}

Second step, call above class from Startup.cs file Configure method. Add this line at bottom of the Configure method.

ApplicationDbInitializer.SeedUsers(userManager, roleManager);

Add dependency injection in Configure method arguments like the code below

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)

Add these two string “UserManager userManager, RoleManager roleManager” in Configure method argument.

In the Startup.cs file need to update ConfigureServices method, create a new service for Identity. Add the following code in ConfigureServices method –

services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>().AddEntityFrameworkStores<AppDbContext>();

Once you run the application, it should create a user, role and assign the user in role in Identity User table.

Happy Coding 🙂