Back to: ASP.NET Core Web API Tutorials
202 HTTP Status Code in ASP.NET Core Web API
In this article, I will discuss How to Return 202 Accepted HTTP Status Code in ASP.NET Core Web API with Examples. Please read our previous article discussing How to Return 201 HTTP Status Code from the ASP.NET Core Web API Controller Action method with Examples.
202 HTTP Status Code
The 202 HTTP Status Code is known as “Accepted”. It is a success response code indicates that the request has been accepted for processing, but the processing has not been completed. It is a way to acknowledge the client that the request has been accepted for processing, but the outcome is not immediately available. This status code is primarily intended for situations where a server takes a significant amount of time to process a request, and the request would need to continue asynchronously. The following are some of the Key Points about 202 HTTP Status Code:
- Request Accepted: The server has received the request and will process it, but the processing may not be completed immediately.
- Not a Guarantee of Success: A 202 status code does not indicate that the final processing of the request will be successful; it states that the initial request was valid and has been accepted for processing. The final outcome of the request should be checked once the processing has been completed.
- Asynchronous Operations: Often used for asynchronous operations where the server will process the request in the background, and the client does not need to wait for the operation to complete.
- Response Payload: The response to a 202 status code typically includes information on how to monitor the progress of the request. This could be in the form of a URL pointing to a status endpoint where the client can obtain information about the ongoing processing or details on how long the client should wait before checking the status.
For example, imagine a situation where a user uploads a large file to a server. Instead of making the user wait for the entire file to be processed, the server could respond immediately with a 202 status code to indicate that the upload has been accepted, and the processing will happen asynchronously.
How to Return 202 HTTP Status Code in ASP.NET Core Web API?
To return a 202 Accepted HTTP Status Code in an ASP.NET Core Web API, we can use the Accepted, AcceptedAtAction, or AcceptedAtRoute methods from our controller action methods. These methods are useful for creating responses that indicate a request has been accepted for processing but the processing is not yet complete. This is useful in scenarios where we want to acknowledge that a request has been received and will be processed, but the process may take a longer time to complete, such as background tasks or operations queued for processing.
Using Accepted in ASP.NET Core Web API:
The Accepted method returns a 202 Accepted status code, which indicates that the request has been accepted for processing but has not been completed. The following six overloaded versions of the Accepted method are available in ASP.NET Core Web API. You can check the same by ControllerBase class class:
- AcceptedResult Accepted(): This version of the Accepted method returns a 202 Accepted status code without any additional content or headers. It’s useful when we need to inform the client that the request has been accepted without needing to provide any further details or follow-up URI.
- AcceptedResult Accepted([ActionResultObjectValue] object? value): This overload returns a 202 Accepted status with a JSON representation of the provided object as the response body. You need to use this when you want to return additional data about the request’s acceptance, such as a message or some data regarding the initial processing.
- AcceptedResult Accepted(Uri uri): This function returns a 202 Accepted status and sets the Location header of the response to the provided Uri. It is suitable when you want to provide a URL where the client can inquire about the request’s status or the processing result once it has been completed.
- AcceptedResult Accepted(string? uri): This is similar to the Uri version, but it takes a string representing the URI. This is useful when the URI is dynamically constructed as a string during runtime. It also sets the Location header to this URI.
- AcceptedResult Accepted(string? uri, [ActionResultObjectValue] object? value): Returns a 202 Accepted status, sets the Location header with the provided string URI, and includes the serialized representation of the provided object in the response body. Use this when you want to provide both a follow-up location and additional data related to the request in the response. This is helpful for giving the client a link to check the status along with some initial data or context.
- AcceptedResult Accepted(Uri uri, [ActionResultObjectValue] object? value): This overload functions the same as the previous one but uses a Uri object for the location instead of a string. This is preferable when you have a Uri object already constructed and you need to include additional response data. It provides a clean way to ensure the URI is valid and well-formed.
Example to Understand Accepted Method:
Let’s understand the above overloaded Accepted methods with an example. Let’s add an Empty API Controller with the name JobController and then copy and paste the following code. The following code is self-explained, so please go through the comment lines for a better understanding.
using Microsoft.AspNetCore.Mvc; namespace ReturnTypeAndStatusCodes.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class JobController : ControllerBase { // POST: api/Job/CreateJobAsyncWithoutData [HttpPost] public async Task<IActionResult> CreateJobAsyncWithoutData() { // Start the asynchronous processing without blocking the thread. // The LongRunningTask will take 120 seconds to complete. // The main thread will not be blocked until the LongRunningTask is completed. LongRunningTask(); // Return 202 Accepted status code without any data. return Accepted(); } // POST: api/Job/CreateJobAsyncWithData [HttpPost] public async Task<IActionResult> CreateJobAsyncWithData() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Create a resource status object to return as part of the response. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours" }; // Return 202 Accepted status code with the resource status data. return Accepted(resourceStatus); } // POST: api/Job/CreateJobWithLocation [HttpPost] public async Task<IActionResult> CreateJobWithLocation() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Assume the Processing JobId is 123. var processingJobId = 123; // Generate the dynamic Location URI to check status using the JobId. var locationUrl = Url.Action("GetStatus", new { JobId = processingJobId }); // Check if the generated location URL is null or empty. if (string.IsNullOrEmpty(locationUrl)) { // Return 400 Bad Request if the URL could not be generated. return BadRequest("Unable to generate status URL."); } // Create a new Uri object from the location URL. var locationUri = new Uri(locationUrl, UriKind.RelativeOrAbsolute); // Return 202 Accepted status code with a location URI. return Accepted(locationUri); } // POST: api/Job/CreateJobWithLocationAndData [HttpPost] public async Task<IActionResult> CreateJobWithLocationAndData() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Assume the Processing JobId is 123. var processingJobId = 123; // Create a resource status object to return as part of the response. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours", JobId = processingJobId }; // Generate the dynamic Location URI to check status using the JobId. var locationUrl = Url.Action("GetStatus", new { JobId = processingJobId }); // Check if the generated location URL is null or empty. if (string.IsNullOrEmpty(locationUrl)) { // Return 400 Bad Request if the URL could not be generated. return BadRequest("Unable to generate status URL."); } // Create a new Uri object from the location URL. var locationUri = new Uri(locationUrl, UriKind.RelativeOrAbsolute); // Return 202 Accepted status code with a location URI and a response body containing the resource status. return Accepted(locationUri, resourceStatus); } // GET: api/Job/GetStatus/123 // Define a GET endpoint named GetStatus which takes a JobId as a parameter. [HttpGet("{JobId}")] public IActionResult GetStatus(int JobId) { // Create a resource status object representing the current status of the job. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours" }; // Return 200 OK status code with the resource status data. return Ok(resourceStatus); } // This is the method that performs the time-consuming asynchronous operation. private async Task LongRunningTask() { // Simulate a long-running task with a 120 seconds delay. await Task.Delay(TimeSpan.FromSeconds(120)); // Task logic goes here (not implemented in this example). } } }
Testing the APIs:
Now, run the application and test the APIs using Postman, Fiddler, Swagger, or .http files. You can test the APIs using Postman as follows: Please change the port number where your application is running:
API1: Returning 202 Status Code without Data
URL: https://localhost:7128/api/Job/CreateJobAsyncWithoutData
Method: POST
Response Body: The Response body will be empty
API2: Returning 202 Status Code with Data
URL: https://localhost:7128/api/Job/CreateJobAsyncWithData
Method: POST
Response Body: The Response body will contain the data
API3: Returning 202 Status Code with Location URL
URL: https://localhost:7128/api/Job/CreateJobWithLocation
Method: Post
Response Body: The response body will be empty
Response Header: The response header will contain the Location URI, which you can use to get the current status.
API4: Returning 202 Status Code with Location URL and Data
URL: https://localhost:7128/api/Job/CreateJobWithLocationAndData
Method: POST
Response Body: The Response body will contain the data
Response Header: The response header will contain the Location URI, which you can use to get the current status.
API5: Checking the Job Status with Location URL
URL: https://localhost:7128/api/Job/GetStatus/123
Method: POST
Response Body: The Response body will contain the data
Using AcceptedAtAction in ASP.NET Core Web API:
In ASP.NET Core Web API, the AcceptedAtAction method is a way to return a 202 Accepted HTTP status code along with additional information about how and where the client can follow up on a request that is being processed asynchronously. This method is helpful for providing a response that includes a URI to the specific action where the results of the processing can be accessed or checked. The following overloaded versions are available in ASP.NET Core:
- AcceptedAtActionResult AcceptedAtAction(string? actionName): This overload returns a 202 Accepted response and sets the Location header to the URL of the specified action within the same controller. It is useful when you do not need to specify the controller name or additional data.
- AcceptedAtActionResult AcceptedAtAction(string? actionName, string? controllerName): This is similar to the first but allows specifying a different controller than the one in which the current method resides. This is useful when the action is in a different controller.
- AcceptedAtActionResult AcceptedAtAction(string? actionName, [ActionResultObjectValue] object? value): This version not only sets the Location header but also includes a serialized representation of the provided object in the response body. This is beneficial for sending additional details about the acceptance or the process being initiated.
- AcceptedAtActionResult AcceptedAtAction(string? actionName, string? controllerName, [ActionResultObjectValue] object? routeValues): This allows you to specify the controller and action and use route values to generate the Location URL dynamically. It is useful when URL parameters are required to access the action.
- AcceptedAtActionResult AcceptedAtAction(string? actionName, object? routeValues, [ActionResultObjectValue] object? value): This overload sets the Location header using the specified action and dynamic route values and includes additional data in the response body. It combines the flexibility of passing route parameters with the ability to return a data object.
- AcceptedAtActionResult AcceptedAtAction(string? actionName, string? controllerName, object? routeValues, [ActionResultObjectValue] object? value): This is the most comprehensive overload, which specifies the controller, action, route values for URL generation, and an object for the response body. This provides maximum flexibility in directing the client to a specific URL while also providing them with the necessary context or details about the process.
Example to Understand AcceptedAtAction in ASP.NET Core Web API:
Let us understand the AcceptedAtAction methods with an example. So, let us modify the JobController as follows. The following code is self-explained, so please go through the comment lines for a better understanding.
using Microsoft.AspNetCore.Mvc; namespace ReturnTypeAndStatusCodes.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class JobController : ControllerBase { // POST: api/Job/CreateJobWithLocation [HttpPost] public async Task<IActionResult> CreateJobWithLocation() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Assume the Processing JobId is 123. var processingJobId = 123; // Return 202 Accepted status code with a location URI pointing to the GetStatus action. // Assuming the job has an ID assigned after being added // Passing object value as null as we don't want to return any data // Here, GetStatus is the Action Method Name return AcceptedAtAction("GetStatus", new { JobId = processingJobId }, null); } // POST: api/Job/CreateJobWithLocationAndData [HttpPost] public async Task<IActionResult> CreateJobWithLocationAndData() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Assume the Processing JobId is 123. var processingJobId = 123; // Create a resource status object to return as part of the response. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours", JobId = processingJobId }; // Return 202 Accepted status code with a location URI and the resource status data. // Here, GetStatus is the Action Method Name and Job is the Controller Name // return AcceptedAtAction("GetStatus", new { JobId = processingJobId }, resourceStatus); return AcceptedAtAction("GetStatus", "Job", new { JobId = processingJobId }, resourceStatus); } // GET: api/Job/GetStatus/123 [HttpGet("{JobId}")] public IActionResult GetStatus(int JobId) { // Create a resource status object representing the current status of the job. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours" }; // Return 200 OK status code with the resource status data. return Ok(resourceStatus); } // This is the method that performs the time-consuming asynchronous operation. private async Task LongRunningTask() { // Simulate a long-running task with a 120 seconds delay. await Task.Delay(TimeSpan.FromSeconds(120)); // Task logic goes here (not implemented in this example). } } }
Using AcceptedAtRoute in ASP.NET Core Web API
In ASP.NET Core Web API, the AcceptedAtRoute method is used to create an AcceptedAtRouteResult that corresponds to the HTTP 202 Accepted status code. This method helps in directing clients to a specific route for follow-up actions or status checks regarding a request that has been accepted but is still being processed. It is similar to AcceptedAtAction but uses route names instead of action names to construct the URI in the Location header. The following are the different overloaded versions of the AcceptedAtRoute method in ASP.NET Core Web API. You can find these methods in the ControllerBase class:
- AcceptedAtRouteResult AcceptedAtRoute([ActionResultObjectValue] object? routeValues): This method returns a 202 Accepted status code and sets the Location header using route values that map to a named route. It’s useful when you do not need to specify a route name explicitly but rely on the routing configuration to match the provided values.
- AcceptedAtRouteResult AcceptedAtRoute(string? routeName): This function returns a 202 Accepted status code and constructs the Location header using the specified named route without additional route values. It is straightforward when the route does not require additional parameters to resolve correctly.
- AcceptedAtRouteResult AcceptedAtRoute(string? routeName, object? routeValues): This overload combines a specific route name with route values to generate the Location URL. It’s particularly useful when you want to direct the client to a complex route that requires parameters.
- AcceptedAtRouteResult AcceptedAtRoute(object? routeValues, [ActionResultObjectValue] object? value): Not only sets the Location header based on the route values but also includes a response body containing serialized data. This version is beneficial if you want to provide the client with additional details about the accepted request or the ongoing processing.
- AcceptedAtRouteResult AcceptedAtRoute(string? routeName, object? routeValues, [ActionResultObjectValue] object? value): This is the most detailed overloaded version. It allows specifying a route by name, including necessary parameters for the route through route values, and sending additional data in the response body. It offers the greatest flexibility, allowing precise control over the URL construction and the information returned to the client.
Example to Understand AcceptedAtRoute in ASP.NET Core Web API
Please modify the Job Controller as follows. The following code is self-explanatory, so please read the comment lines to understand it better.
using Microsoft.AspNetCore.Mvc; namespace ReturnTypeAndStatusCodes.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class JobController : ControllerBase { //POST: api/Job/CreateJobWithLocation [HttpPost] public async Task<IActionResult> CreateJobWithLocation() { // Logic to start asynchronous processing and we are not blocking the thread LongRunningTask(); //Let is asume the Processing JobId is 123 var processingJobId = 123; // Assuming the job has an ID assigned after being added // GetJobStatus is the Route Name return AcceptedAtRoute("GetJobStatus", new { JobId = processingJobId }); } // POST: api/Job/CreateJobWithLocationAndData [HttpPost] public async Task<IActionResult> CreateJobWithLocationAndData() { // Start the asynchronous processing without blocking the thread. LongRunningTask(); // Assume the Processing JobId is 123. var processingJobId = 123; // Create a resource status object to return as part of the response. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours", JobId = processingJobId }; // Return 202 Accepted status code with a location URI and the resource status data. return AcceptedAtRoute(new { controller = "Job", action = "GetStatus", JobId = processingJobId }, resourceStatus); } // GET: api/Job/GetStatus/123 //We have assigned the Route Name as GetJobStatus [HttpGet("{JobId}", Name = "GetJobStatus")] public IActionResult GetStatus(int JobId) { // Create a resource status object representing the current status of the job. var resourceStatus = new { Status = "Processing", EstimatedCompletionTime = "2 hours" }; // Return 200 OK status code with the resource status data. return Ok(resourceStatus); } // This is the method that performs the time-consuming asynchronous operation. private async Task LongRunningTask() { // Simulate a long-running task with a 120 seconds delay. await Task.Delay(TimeSpan.FromSeconds(120)); // Task logic goes here (not implemented in this example). } } }
When should you use the 202 Accepted Status Code in ASP.NET Core Web API?
The 202 Accepted status code in ASP.NET Core Web API should be used in scenarios where a request has been received and understood by the server, but the processing has not been completed yet. It indicates that the request has been accepted for processing, but the processing will take some time, and the response will not be available immediately. The following are some of the scenarios where we need to return 202 Accepted Status Code:
- Long-Running Processes: When a request initiates a long-running process, the client should not expect an immediate response with the final result. For example, starting a background job or a batch-processing task that takes time to complete. The 202 Accepted status code lets the client know that the request has been accepted, and the process will be completed in the background.
- Asynchronous Processing: This is when the server needs to perform a long-running operation, and you want to inform the client that their request has been accepted and will be processed asynchronously. For example, submitting a job for batch processing, file uploads, or background data processing.
- Queued Requests: When the server places the request in a queue for later processing. The client is informed that the request is queued and will be processed eventually. For example, order processing systems where orders are queued for fulfillment.
- Deferred Operations: When the actual processing will happen later, and the client should check back later for the status or result of the operation. For example, Scheduling tasks or events that will be executed in the future.
- Partial Processing: When the initial part of the request has been processed, but further steps are needed to complete it. For example, starting a multi-step transaction or workflow where the initial step is accepted, but subsequent steps need to be processed later.
When Should We Use Accepted vs AcceptedAtAction vs AcceptedAtRoute?
In ASP.NET Core Web API, Accepted, AcceptedAtAction, and AcceptedAtRoute are used to return a 202 Accepted HTTP status code, but each is suited for different scenarios. Here’s when to use each method:
Accepted:
When you want to acknowledge the request but don’t need to provide additional information about where to check the status or how to retrieve the resource. It’s the simplest form of returning a 202 Accepted response. When a request has been accepted for processing but no immediate result is available, and you do not need to provide a URL for status checking or further actions.
AcceptedAtAction:
When you want to return a 202 Accepted status code, include a Location header pointing to a specific action method where the client can check the status or get more details. This is useful when you have a specific action that provides more information or status about the process initiated. When the request initiates a process, and you want to provide a URL to another action within the same controller or a different controller where, the client can check the status or get more details.
AcceptedAtRoute:
When you want to return a 202 Accepted status code, include a Location header pointing to a specific route defined in your routing configuration. This is useful when you have named routes or specific routes that are not tied to a particular controller action. When you want to provide a URL to a route (possibly outside the current controller) where the client can check the status or get more details. You might use this when your routing is more complex or when you have named routes.
In the next article, I will discuss how to Return the 204 HTTP Status Code in the ASP.NET Core Web API with Examples. In this article, I try to explain how to Return the 202 Accepted HTTP Status Code from the ASP.NET Core Web API Application with Examples. I hope you enjoy this article on “202 HTTP Status Code in the ASP.NET Core Web API.”