Back to: ASP.NET Core Web API Tutorials
304 HTTP Status Code in ASP.NET Core Web API
In this article, I will discuss how to Return the 304 “Not Modified” HTTP Status Code from the ASP.NET Core Web API Controller Action method with Examples. Please read our previous article on how to Return 302 HTTP Status Code in ASP.NET Core Web API with Examples.
What is 304 HTTP Status Code?
The HTTP 304 status code, also known as “Not Modified,” is used in web communication to indicate that the requested resource has not been modified since the last time it was requested. This response is typically used with conditional GET requests to reduce bandwidth and processing requirements. It is part of the HTTP caching mechanism.
How 304 HTTP Status Code Works?
- Initial Request: When a client (such as a web browser) makes a request to a server, it can include headers like If-Modified-Since (The date and time when the client last received the resource) or If-None-Match (An entity tag (ETag) value that represents a specific version of the resource) in the request. These headers contain the timestamp of the last modification or an ETag (Entity Tag, i.e., the version) value of the resource.
- Server Response: Open the receives the request, the server compares the provided timestamp or ETag with the current version of the resource. If the resource has not been modified since the specified date or ETag, the server responds with a 304 Status Code, indicating the cached version is still valid. No Response body content is sent with the response, reducing the amount of data transmitted.
- Client Action: Upon receiving a 304 response, the client knows to use the version of the resource it already has in its cache rather than downloading it again.
Benefits of Using 304 Not Modified
This mechanism is particularly useful in reducing bandwidth usage and improves performance by avoiding unnecessary data transfers. The client can continue using the cached version of the resource without having to re-download it.
- Bandwidth Savings: Since the server does not send the resource again, it saves bandwidth.
- Faster Load Times: The client can use the cached version, leading to faster page load times.
- Reduced Server Load: The server does not need to reprocess and resend resources that have remained the same, reducing its workload.
How to Implement 304 HTTP Status Code in ASP.NET Core Web API?
Implementing the 304 HTTP status code in an ASP.NET Core Web API involves using caching mechanisms and setting appropriate headers to allow the client to make conditional requests. The following are steps to implement in an ASP.NET Core Web API.
- Add Response Caching Middleware
- Configure Response Caching in the Controller
- Use ETag and Last-Modified Headers
Note: We will discuss Response Caching in detail in our upcoming articles when we discussed Caching. Here, we are focusing on 304 HTTP Status Code.
Add Response Caching Middleware:
First, modify the Program.cs class file is as follows, including the response caching middleware. Here, we are adding a Response Caching service to the built-in dependency injection container and a Response Caching middleware component to the Request Processing Pipeline.
namespace ReturnTypeAndStatusCodes { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); //Adding Response Caching Services builder.Services.AddResponseCaching(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); //Adding Response Caching Middleware Component app.UseResponseCaching(); app.MapControllers(); app.Run(); } } }
Creating a Resource Model:
Create a class file named Resource.cs within the Models folder, and then copy and paste the following code. This will be our model used to return the data to the client from the server:
namespace ReturnTypeAndStatusCodes.Models { public class Resource { public int Id { get; set; } public string Content { get; set; } public DateTime LastModified { get; set; } } }
Configure Response Caching in the Controller
Use the ResponseCache attribute in your controller actions to set caching parameters. So, create an Empty API Controller named Sample Controller and then copy and paste the following code:
using Microsoft.AspNetCore.Mvc; using ReturnTypeAndStatusCodes.Models; namespace ReturnTypeAndStatusCodes.Controllers { [ApiController] [Route("api/[controller]")] public class SampleController : ControllerBase { // Defines an action method that returns an IActionResult and takes an 'id' parameter // Specifies that this action responds to HTTP GET requests and expects an 'id' parameter in the URL [HttpGet("{id}")] // Enables response caching for 60 seconds on the client side [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client, NoStore = false)] public IActionResult Get(int id) { // Fetches the resource by ID using the GetResourceById method var resource = GetResourceById(id); // Checks if the resource is null (not found) if (resource == null) { // Returns a 404 Not Found response if the resource is null return NotFound(); } // Converts the LastModified property of the resource to UTC time var lastModified = resource.LastModified.ToUniversalTime(); // Generates an ETag using the ticks of the LastModified property var eTag = $"{lastModified.Ticks}"; // Checks if the request contains the If-None-Match header and if it matches the generated ETag if (Request.Headers.ContainsKey("If-None-Match") && Request.Headers["If-None-Match"].ToString() == eTag) { // Returns a 304 Not Modified status if the ETag matches return StatusCode(StatusCodes.Status304NotModified); } // Checks if the request contains the If-Modified-Since header and if the resource has not been modified since that time if (Request.Headers.ContainsKey("If-Modified-Since") && DateTime.TryParse(Request.Headers["If-Modified-Since"], out DateTime ifModifiedSince) && ifModifiedSince >= lastModified) { // // Returns a 304 Not Modified status if the LastModified date is earlier than or equal to the If-Modified-Since dat return StatusCode(StatusCodes.Status304NotModified); } // Adds the ETag header to the response Response.Headers["ETag"] = eTag; // Adds the Last-Modified header to the response using RFC1123 format. Response.Headers["Last-Modified"] = lastModified.ToString("R"); // Returns a 200 OK status with the resource as the response body return Ok(resource); } // Simulates fetching a resource by ID private Resource GetResourceById(int id) { // Returns a new Resource object with a hardcoded last modified date return new Resource { Id = id, Content = "Sample Content", // Sample content LastModified = Convert.ToDateTime("04-08-2024 04:24:37") // Example last modified time }; } } }
Here,
- The Get method retrieves a resource by its ID and returns it with appropriate caching headers.
- The ResponseCache attribute specifies that the response can be cached for 60 seconds.
- The ETag and Last-Modified headers are used to implement conditional GET requests.
- If the resource has not been modified since the client’s cached version, a 304 Not Modified status is returned.
- The GetResourceById method simulates fetching a resource from a data source.
Use ETag and Last-Modified Headers
The above example demonstrates how to use the ETag and Last-Modified headers:
- ETag Header: A unique identifier for the version of the resource.
- Last-Modified Header: The date and time when the resource was last modified.
Testing the Implementation
Initial Request:
The client makes an initial request to get the resource. Let us test this using Postman.
- Open Postman.
- Create a new GET request.
- Enter the URL of your API endpoint, e.g., https://localhost:7247/api/sample/1.
- Send the request, and you should get a 200 OK Response with the response body shown in the image below.
Now, please observe the response headers. You should see that the server sets the ETag and Last-Modified header values, as shown in the image below. Please copy the ETag and Last-Modified header values, which we will use in our subsequent requests.
Subsequent Request with If-None-Match Header:
The client sends a request with the If-None-Match header. The server checks the If-None-Match header and responds with 304 Not Modified if the resource has not changed.
- Copy the value of the ETag header from the initial response.
- Create a new GET request in Postman.
- Enter the same URL.
- Go to the “Headers” tab in Postman.
- Add a new header with the key If-None-Match and the value of the ETag copied from the initial response.
- Send the request.
If the resource has not changed, you should receive a 304 Not Modified status code as shown in the below image:
Subsequent Request with If-Modified-Since Header:
The client sends a request with the If-Modified-Since header. The server checks the If-Modified-Since header and responds with 304 Not Modified if the resource has not changed.
- Copy the value of the Last-Modified header from the initial response.
- Create a new GET request in Postman.
- Enter the same URL.
- Go to the “Headers” tab in Postman.
- Add a new header with the key If-Modified-Since and the value of the Last-Modified header copied from the initial response.
- Send the request.
If the resource has not changed, you should receive a 304 Not Modified status code, as shown in the image below.
When Should We Use 304 HTTP Status Code in ASP.NET Core Web API?
The 304 HTTP status code, “Not Modified,” is useful in ASP.NET Core Web APIs to optimize performance and bandwidth by utilizing caching mechanisms. The following are some of the situations where using the 304 HTTP Status Code is beneficial:
- Static Resources: When serving static resources like images, CSS, JavaScript files, etc., that do not change frequently.
- API Endpoints with Unchanged Data: When an API endpoint returns data that does not change often, such as configuration settings, reference data, or user profile information.
- Improving Client Performance: This is when you want to reduce clients’ load time by allowing them to use cached versions of resources instead of re-fetching the entire content from the server.
- Reducing Server Load: This is when you want to minimize the server’s processing load by avoiding the generation and transfer of unchanged data.
- Optimizing Bandwidth Usage: Optimizing bandwidth usage by preventing the re-transfer of unchanged data is especially beneficial for clients with limited bandwidth.
In the next article, I will discuss how to Return 400 HTTP Status Code in ASP.NET Core Web API with Examples. In this article, I try to explain how to Return 304 Not Modified HTTP Status Code in ASP.NET Core Web API with Examples. I hope you enjoy this article on “3304 Not Modified HTTP Status Code in the ASP.NET Core Web API.”