Navigation
Recherche
|
How to implement idempotent APIs in ASP.NET Core
jeudi 20 mars 2025, 10:00 , par InfoWorld
When designing your APIs, you should make them idempotent to ensure that they are robust, reliable, and fault-tolerant. An operation is idempotent when repeating it will always result in the same outcome. For example, an elevator button is idempotent. No matter how many times you press the same elevator button, the elevator will make one trip to the designated floor. Your APIs should work the same way.
In this article, we’ll examine how to build idempotent APIs in ASP.NET Core with relevant code examples to illustrate the concepts covered. To use the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here. Why do we need idempotent APIs? Idempotent APIs ensure that duplicate requests will yield one and the same result. For example, the HTTP methods GET, HEAD, OPTIONS, and TRACE are idempotent because they do not modify the state of a resource in the server. Instead, they fetch the relevant resource metadata or its representation. Let us understand the importance of API idempotency with an example. In a typical shopping cart application, a user often needs to make an API call to create a new order. If the API request is successful, the user should get a confirmation. However, a network issue might prevent the user from receiving confirmation, even if the user made the API request. In this case, the user might want to recreate the same order, i.e., retry the same API call, because they did not receive a confirmation after the previous request to create a new order. What if the same order is created more than once? Clearly, we want to avoid duplicating orders. You should design your shopping cart API to be idempotent to avoid creating duplicate orders when a user makes multiple retries to the same API endpoint. In general, you should make your APIs idempotent in order to prevent duplicate requests and retries from putting your application in an erroneous state. In other words, idempotent APIs help make your application more robust, reliable, and fault-tolerant. Understand idempotency in HTTP methods Note that the HTTP POST and HTTP PATCH methods are not idempotent. However, the HTTP methods GET, HEAD, PUT, and DELETE are idempotent by design. HTTP GET: The HTTP GET operation is the most widely used HTTP method. HTTP GET retrieves data from the server but does not alter the state of the resource. Hence, the HTTP GET method call is idempotent. HTTP HEAD: You use the HTTP HEAD method to retrieve the metadata of a resource. This method is typically used to determine if a resource is available on the server. If the resource exists, invoking the HTTP HEAD method will return the size and last modified date of the resource. In contrast to the HTTP GET method, the HTTP HEAD method does not return the message body as part of the response. HTTP PUT: You use the HTTP PUT method to update existing data on the server. Remember that this operation will alter the state of a resource the first time only. Subsequent PUTs will not alter the state of the resource, but simply overwrite the state with the same input. For example, if you update a record in a database, the update should first check if the record exists. If it exists, the data stored in the database should be replaced by the new data. However, if you make multiple calls to the same API method with the same input, the data stored in the database will remain the same. HTTP DELETE: You can delete a resource several times, but only the first deletion will change the state of the system. Because invoking one or multiple HTTP DELETE operations has the same result, the method is idempotent. If you delete a resource that does not exist, the method should return a message stating that the resource has already been deleted or not found. HTTP POST: The HTTP POST method is used to send data to the server for processing. Because a POST operation can create a new resource on the server, it is never idempotent. Multiple POST calls can result in multiple new resources. HTTP PATCH: The HTTP PATCH method is used to modify a resource on the server without altering the entire resource. This method is not idempotent because repeated HTTP PATCH requests can change the state of the resource repeatedly. For example, if you make a HTTP PATCH request to reduce the quantity of an item in inventory, the available stock of the item will be reduced with each repetition of the request. Create an ASP.NET Core Web API project in Visual Studio 2022 To create an ASP.NET Core 9 Web API project in Visual Studio 2022, follow the steps outlined below. Launch the Visual Studio 2022 IDE. Click on “Create new project.” In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Optionally check the “Place solution and project in the same directory” check box, depending on your preferences. Click Next. In the “Additional Information” window shown next, select “.NET 9.0 (Standard Term Support)” as the framework version and ensure that the “Use controllers” box is checked. We will be using controllers in this project. Elsewhere in the “Additional Information” window, leave the “Authentication Type” set to “None” (the default) and make sure the check boxes “Enable Open API Support,” “Configure for HTTPS,” and “Enable Docker” remain unchecked. We won’t be using any of those features here. Click Create. We’ll use this ASP.NET Core Web API project in the sections below. Custom logic for idempotent APIs In this section, we’ll examine how we can build an idempotent RESTful API in ASP.NET Core. For the sake of simplicity and brevity, we’ll only create one HTTP POST action method in our controller class and skip creating other action methods. As we already know, HTTP POST methods are not idempotent by design because they are used to process data or create new resources. However, we can make them idempotent by writing custom logic. The following sequence of steps illustrates the logic: The client creates a unique key with each request and sends it to the server in a custom header. When the server receives the request, it checks whether the key is new or already exists. If the key is new, the server processes the request and saves the result. If the key already exists, the server returns the result of the stored operation without processing the request again. In short, we assign a unique key to each request that allows the server to determine whether the request has already been processed. In this way, we ensure that each request is processed once and only once. Now let’s get started with our implementation. For our example, we’ll use a simple shopping cart application. Create the model classes In the project we created earlier, create the following classes in the Models folder. public class Product { public int Product_Id { get; set; } public string Product_Code { get; set; } public string Product_Name { get; set; } public double Product_Price { get; set; } } public class Order { public int Order_Id { get; set; } public List Products { get; set; } } public class KeyStore { public string Key { get; set; } public DateTime Expiry { get; set; } } While the Product and Order classes are typically used in a ShoppingCart application, the KeyStore class is used here to store our idempotency keys. In this implementation, we’ll save these keys in the database (dbContext). Naturally, you could change the implementation to store the keys in the cache or any other data store. Create the controller class Right-click on the Controllers folder in the Solution Explorer Window and create an API controller called OrderController. Now, enter the following action method in the OrderController class. This method creates a new order. [HttpPost] public IActionResult CreateOrder([FromBody] Order order, [FromHeader(Name = 'X-Idempotency_Key')] string key) { if (string.IsNullOrEmpty(key)) { return BadRequest('Idempotency key is required.'); } if (_dbContext.KeyStore.FirstOrDefault(k => k.Key == key)!= null) { var existingItem = _dbContext.Orders.FirstOrDefault(o => o.Order_Id == order.Order_Id); return Conflict(new { message = 'Request has already been processed.', item = existingItem }); } _dbContext.KeyStore.Add(new KeyStore {Key = key, Expiry = DateTime.Now.AddDays(7)}); _dbContext.Add(order); _dbContext.SaveChanges(); return Ok(order.Order_Id); } Examine the code above. An idempotency key is generated at the client side and passed in the request header. This key will be used by the server to ensure that repeated calls to the same action method will not create duplicate records in the database. In other words, if the key is already present in the KeyStore, then the request for creation of the resource will be ignored. The presence of the key in the KeyStore means that the request was already processed earlier. Takeaways By embracing idempotency, you can build APIs that are robust, reliable, and fault-tolerant. Idempotent APIs are particularly important and beneficial in distributed systems, where network issues might lead to large numbers of retried requests from the client side. That said, you should always validate input data to ensure data consistency before storing data in the database.
https://www.infoworld.com/article/3847079/how-to-implement-idempotent-apis-in-asp-net-core.html
Voir aussi |
56 sources (32 en français)
Date Actuelle
mer. 26 mars - 09:38 CET
|