HTTP PATCH Method in ASP.NET Core Web API

How to Implement HTTP PATCH Method in ASP.NET Core Web API

In this article, I will discuss how to implement the HTTP PATCH method in the ASP.NET Core Web API Application using real-time examples. Please read our previous article discussing the HTTP PUT Method in ASP.NET Core Web API with Examples.

HTTP PATCH Method in ASP.NET Core Web API

The HTTP PATCH Method in ASP.NET Core Web API is used to partially update an existing resource. Unlike PUT, which is used to replace an entire resource, PATCH applies a partial update, making it more efficient for updating only specific fields of a resource. This method is useful in scenarios where sending the complete resource representation would be unnecessary or costly regarding bandwidth.

Characteristics of HTTP PATCH Method
  • Partial Update: The PATCH HTTP Request is used to make partial updates. For example, if you have an object representing a user with names, email, and address fields, a PATCH request might change just the email field without affecting the rest.
  • Idempotency: PATCH requests are supposed to be idempotent, meaning that multiple identical PATCH requests should have the same effect as a single request. However, in real-time, this depends on the nature of the operation and how it’s implemented on the server. Some PATCH operations might not be idempotent if they apply changes in a way that can vary with each request.
How Do We Implement HTTP PATCH Method in ASP.NET Core Web API?

To implement the PATCH method in an ASP.NET Core Web API, you typically follow these steps:

  • Define the Resource Model: This class represents the data structure of the resource you are working with.
  • Create the DTO (Data Transfer Object): This is often a subset or a variation of the resource model for the specific needs of a web API endpoint. For a PATCH operation, the DTO might include only the fields that can be updated.
  • Create a Controller: Implement a controller class derived from the ControllerBase class. This controller will contain the action method for handling PATCH requests.
  • Define the PATCH Action Method: Inside the controller, define an action method specifically for handling PATCH requests. Use the HttpPatch attribute to mark this method for handling PATCH requests and specify the route if necessary.
  • Use JSON Patch: ASP.NET Core supports JSON Patch (RFC 6902), which defines a JSON format for expressing a sequence of operations to apply to a JSON document; this can be used to describe the changes to apply through a PATCH request.
  • Implement the Patch Logic: Within the PATCH action method, implement the logic to update an existing resource partially using the data received from the client. This involves parsing the JSON Patch document, applying the changes to an existing resource, and then saving those changes to the database.
  • Return a Response: The action method should return an appropriate response depending on the outcome. Common responses include OK (200) if the update was successful, NotFound (404) if the resource does not exist, and BadRequest (400) if the request data is invalid.

Let us see an example of implementing a PATCH request in an ASP.NET Core Web API Application.

Creating Product Model

First, define a model that represents the resource. So, create a class file named Product.cs and copy and paste the following code. Here, we have marked the question mark with the Description property, meaning this is an optional field, and the rest are mandatory. We have also marked them with the Required Data Annotation Attribute with Name, Price, and Quantity properties.

using System.ComponentModel.DataAnnotations;

namespace HTTPMethodDemo.Models
{
    public class Product
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public int Price { get; set; }
        [Required]
        public int Quantity { get; set; }
        public string? Description { get; set; }
    }
}
Create Product Service

Create a class file named ProductRepository.cs, and copy and paste the following code. This is the repository where we will do all database operations related to the Product model.

namespace HTTPMethodDemo.Models
{
    public class ProductRepository
    {
        public List<Product> Products = new List<Product>()
        {
            new Product() { Id = 1, Name= "Laptop", Price = 1000, Quantity = 10, Description = "A high-performance Laptop." },
            new Product() { Id = 2, Name= "Desktop", Price = 2000, Quantity = 20 } //Description is Optional
        };

        public Product GetById(int Id)
        {
            // Find a product by ID
            return Products.FirstOrDefault(p => p.Id == Id);
        }

        public void Update(Product product)
        {
            // Find the index of the product to update
            var index = Products.FindIndex(p => p.Id == product.Id);
            if (index != -1)
            {
                // Update the product at the found index
                Products[index] = product;
            }
        }
    }
}
Understanding the Update Method:
  • Return Type: The method returns no value, indicating that its primary purpose is to perform an action rather than compute and return data.
  • Parameter: The method takes a Product object as a parameter, which contains the updated information for a product.
  • FindIndex: This method searches the product list for the product that matches the given ID. It returns the zero-based index of the first occurrence of a product that meets this condition. If no product is found, FindIndex returns -1. Here, we specified the predicate condition using p => p.Id == product.Id lambda expression to find the product whose Id property matches the Id of the provided product parameter.
  • Index Check: The method then checks if the index is not equal to -1, which would indicate that a matching product was found in the list. If a product is not found, the method does nothing, effectively preventing an update on a non-existent product.
  • Update Operation: If a matching product is found (index is not -1), the method updates the product at the found index with the new product object passed into the method. This operation overwrites the existing product data with the updated data contained in the product parameter.
Register Product Repository Service:

Please add the following statement within the Program.cs class file. This will add the service into the dependency injection container to inject the service into the controller and use the service implementations.

builder.Services.AddScoped<ProductRepository>();

Implementing the PATCH Endpoint

To implement the PATCH endpoint in your controller, we need to use Microsoft.AspNetCore.JsonPatch package, which supports JSON Patch operations according to RFC 6902. So, first, install the Microsoft.AspNetCore.JsonPatch package by executing the following code in the Package Manager Console:

Install-Package Microsoft.AspNetCore.JsonPatch

Again, please make sure you have installed the Microsoft.AspNetCore.Mvc.NewtonsoftJson package. This package is needed for JsonPatchDocument support in ASP.NET Core 3.x and above. You can add this package via NuGet Package Manager or by running the following command in your package manager console:

Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson

Once the above Packages are installed, you should see the following:

How to Implement HTTP PATCH Method in ASP.NET Core Web API?

What is JsonPatchDocument in ASP.NET Core Web API?

JsonPatchDocument is part of the Microsoft.AspNetCore.JsonPatch namespace provides support for applying JSON Patch documents to .NET objects. The class offers a way to model the list of operations (adding, removing, replacing, copying, and moving) in a JSON Patch request and apply them to an object model in your ASP.NET Core Web API. Here is how JsonPatchDocument can be used in an ASP.NET Core Web API:

  • Defining a Model: First, you define a model representing the data structure of the resource you are working with.
  • Accepting a Patch Request: In your API controller, you define an endpoint that accepts a PATCH HTTP request. The body of this request contains a JSON Patch document, which is modeled as a JsonPatchDocument<T> parameter, where T is the type of your model.
  • Applying the Patch: You then apply the JsonPatchDocument to an instance of your model. This is typically done after fetching the existing resource from a database or store. The ApplyTo method of JsonPatchDocument is used for this purpose.
  • Validation and Persistence: After applying the patch, you can validate the updated model state and persist the changes back to the database or store.
Register NewtonsoftJson Service:

To use JSON Patch, we must register the NewtonsoftJson service into the dependency injection container. So, please add the following statement within the Program.cs class file. This will add the NewtonsoftJson service into the dependency injection container.

builder.Services.AddControllers().AddNewtonsoftJson();

Creating Controller with PATCH Action Method:

Create an Empty API Controller within the Controllers folder named ProductController and then copy and paste the following code. This Product Controller will contain the action method that handles PATCH requests.

using HTTPMethodDemo.Models;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;

namespace HTTPMethodDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly ProductRepository _productRepository;

        public ProductController(ProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        [HttpPatch("{Id}")]
        public IActionResult PatchProduct(int Id, [FromBody] JsonPatchDocument<Product> patchDoc)
        {
            if (patchDoc == null)
            {
                return BadRequest();
            }

            var product = _productRepository.GetById(Id);

            if (product == null)
            {
                return NotFound();
            }

            patchDoc.ApplyTo(product, ModelState);

            //The following will not work
            //if (!ModelState.IsValid)
            //{
            //    return BadRequest(ModelState);
            //}

            if (!TryValidateModel(product))
            {
                return BadRequest(ModelState);
            }

            _productRepository.Update(product);

            return Ok(product);
        }
    }
}
Understanding the PatchProduct Method:

Public: This method is publicly accessible, which means it can be called as part of the API.

IActionResult: The return type, IActionResult, enables the method to return different types of HTTP responses, making it versatile for indicating the outcome of the PATCH operation (e.g., success, error, not found).

Parameters:
  • int Id: The unique identifier for the product to update. This is passed in the URL of the PATCH request.
  • [FromBody] JsonPatchDocument<Product> patchDoc: A JsonPatchDocument<Product> parameter that represents the set of operations to apply to the product. The [FromBody] attribute indicates that this parameter should be bound from the request body.
Method Logic
if (patchDoc == null)
{
    return BadRequest();
}

First, the method checks if patchDoc is null, which would indicate that no patch document was sent with the request. In this case, the method returns a BadRequest result, signaling an invalid request.

var product = __productRepository.GetById(id);
if (product == null)
{
    return NotFound();
}

The method attempts to retrieve the product to be updated using the GetById method of _productRepository with the provided ID. If no product is found (i.e., product is null), the method returns a NotFound result.

patchDoc.ApplyTo(product, ModelState);

The ApplyTo method of patchDoc is called for applying the patch operations to the retrieved product. The ModelState is passed as a parameter to ApplyTo, allowing the method to add model state errors if the patch document contains invalid operations.

When using JsonPatchDocument<T> and the ApplyTo method, the model validation attributes (like [Required], [Range], etc.) on your model’s properties are not automatically checked against the patch operations. This is because the ApplyTo method modifies the model after the model binding process, which is where validation typically occurs.

After calling ApplyTo, you can manually validate the resulting model instance. This can be done by checking conditions programmatically in your code.

if (!TryValidateModel(product))
{
    return BadRequest(ModelState);
}

_productRepository.Update(product);

After successfully applying the patch document to the product, the Update method of _productRepository is called to update the product in the data store (which, in this example, is an in-memory collection).

return Ok(product);

Finally, the method returns an OK result with the updated product, indicating that the PATCH operation was successful.

How to Test the Patch End Point:

Passing JSON data to a PATCH endpoint in ASP.NET Core Web API that accepts a JsonPatchDocument is straightforward but requires adherence to the JSON Patch format specified in RFC 6902. This format defines a series of operations (add, remove, replace, move, and copy) that describe how to modify a JSON document.

Your JSON data should be an array of operation objects. Each operation object should have at least three properties: op (the operation), path (the location within the target document to apply the operation), and value (the value to use with the operation, if applicable). For example, we have the following Product model:

How to Test the Patch End Point

If you want to update the Name and remove the Description property value of an existing product using JSON Patch, then your JSON Patch document should look like the following:

[
  {
    "op": "replace",
    "path": "/Name",
    "value": "Name Updated"
  },
  {
    "op": "remove",
    "path": "/Description"
  }
]

Here, op represents the operation to be performed; path represents the property name on which the operation will performed. In the case of the replace operation, we need to provide the replaced value using the value key. In the case of removal, we do not need to pass the value key. Now, run the application and test the Patch API endpoint, and it should work as expected:

API: Partial Update of an Existing Product

URL: https://localhost:7047/api/Product/1

Method: PATCH

Request Body:

[
  {
    "op": "replace",
    "path": "/Name",
    "value": "Name Updated"
  },
  {
    "op": "remove",
    "path": "/Description"
  }
]
Using Postman:

How to Implement the HTTP PATCH Method in ASP.NET Core Web API Application with Real-Time Examples

As you can see in the above image, it successfully updates the name property value and removes the description property value of an existing object. In this case, it removes the description property value as it is an optional field. Now, let us try to remove a mandatory field value, i.e., the Name property value, and see whether it is working or not.

Request Body:

[
  {
    "op": "remove",
    "path": "/Name"
  }
]
Using Postman:

How to Implement the HTTP PATCH Method in ASP.NET Core Web API

Now, let us add the description property field value and see whether it is working or not.

Request Body:

[
  {
    "op": "add",
    "path": "/Description",
    "value": "Description Added"
  }
]
Using Postman:

HTTP PATCH Method in ASP.NET Core Web API

Implementing Asynchronous HTTP PATCH Method in ASP.NET Core Web API:

In most real-time applications, we must make the action methods or services async if they perform I/O operations. Let us see how we can implement this.

Modifying the ProductRepository:

First, modify the ProductRepository.cs class file as follows. Here, we are making both the GetById and Update methods async. We are delaying the execution of the action method by 1 second using the await keyword. Let’s assume this is the time period that the server will take to communicate with the database and perform the operation. Also, we changed the method name by appending the word Async.

namespace HTTPMethodDemo.Models
{
    public class ProductRepository
    {
        public List<Product> Products = new List<Product>()
        {
            new Product() { Id = 1, Name= "Laptop", Price = 1000, Quantity = 10, Description = "A high-performance Laptop." },
            new Product() { Id = 2, Name= "Desktop", Price = 2000, Quantity = 20 } //Description is Optional
        };

        public async Task<Product> GetByIdAsync(int Id)
        {
            await Task.Delay(TimeSpan.FromSeconds(1));
            // Find a product by ID
            return Products.FirstOrDefault(p => p.Id == Id);
        }

        public async Task UpdateAsync(Product product)
        {
            await Task.Delay(TimeSpan.FromSeconds(1));

            // Find the index of the product to update
            var index = Products.FindIndex(p => p.Id == product.Id);
            if (index != -1)
            {
                // Update the product at the found index
                Products[index] = product;
            }
        }
    }
}
Modifying the Product Controller:

Next, modify the Product Controller as follows to make the action method Asynchronous. Also, while calling the ProductRepository services, we are using the await keyword so that the Main Thread will not be blocked.

using HTTPMethodDemo.Models;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;

namespace HTTPMethodDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly ProductRepository _productRepository;

        public ProductController(ProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        [HttpPatch("{Id}")]
        public async Task<IActionResult> PatchProduct(int Id, [FromBody] JsonPatchDocument<Product> patchDoc)
        {
            if (patchDoc == null)
            {
                return BadRequest();
            }

            var product = await _productRepository.GetByIdAsync(Id);

            if (product == null)
            {
                return NotFound();
            }

            patchDoc.ApplyTo(product, ModelState);

            //The following will not work
            //if (!ModelState.IsValid)
            //{
            //    return BadRequest(ModelState);
            //}

            if (!TryValidateModel(product))
            {
                return BadRequest(ModelState);
            }

            await _productRepository.UpdateAsync(product);

            return Ok(product);
        }
    }
}

With the above changes in place, run the application and test the endpoint by issuing an HTTP PATCH Request, and it should work as expected.

When Should We Use HTTP PATCH Method in ASP.NET Core Web API?

The HTTP PATCH method is used in ASP.NET Core Web API to apply partial updates to resources. Here’s when and why you should consider using PATCH in your API:

  • Updating Partial Resources: Use PATCH when you need to update one or a few fields of a resource without sending the entire resource. This is useful in scenarios where resources have many fields, and only a small subset needs to be updated, reducing the amount of data that needs to be sent over the network.
  • Reducing Bandwidth and Load: PATCH can help reduce bandwidth usage and server load, allowing clients to send only the changes rather than the complete representation. This is particularly beneficial for applications with limited bandwidth or resources.
  • Supporting Idempotent Operations: While PATCH requests are not inherently idempotent (meaning multiple identical PATCH requests may have different effects), they can be designed to be idempotent. This is useful for operations that need to guarantee safe retries of requests without unintended side effects.
  • Compliance with REST Principles: In a RESTful API, using the correct HTTP methods for their intended purposes is important for compliance with REST principles. PATCH is specifically designed for partial updates, making it the appropriate choice for such operations within RESTful services.

HTTP PUT vs HTTP PATCH in ASP.NET Core Web API

In the context of ASP.NET Core Web API, both HTTP PUT and PATCH methods are used to update resources, but they do so in different ways. Understanding the distinction between these two methods is crucial for designing RESTful APIs and ensuring they adhere to the principles of the HTTP protocol.

HTTP PUT Method/Request
  • Idempotence: A key characteristic of the PUT method is that it is idempotent. This means that making multiple identical requests has the same effect as making a single request. This is important for the reliability of web communications, as it allows for operations to be retried without the risk of unintended side effects.
  • Complete Update: PUT requests are used to update an existing resource entirely. When you use PUT, you send the complete updated entity to the server. This means that the request must include all entity attributes, not just those being changed. If an attribute is omitted, it is assumed that the attribute is being set to its default value or being removed.
  • Usage: Use PUT when you want to update a resource and have all the new data for the resource. It’s commonly used for scenarios where the client replaces an existing resource with a new version.
HTTP PATCH Method/Request
  • Partial Update: Unlike PUT, PATCH is used for partial resource updates. This means the request only needs to include the changes to the resource, not the complete resource. This is more efficient when only a few attributes of a resource need to be updated.
  • Not Necessarily Idempotent: PATCH can be non-idempotent, which means consecutive requests may have different effects. However, it’s possible to design a PATCH operation in an idempotent manner.
  • Payload: The request body of a PATCH request often follows a specific format (such as JSON Patch or JSON Merge Patch) that specifies the changes to be applied to the resource.
  • Usage: Use PATCH when you need to update a subset of a resource’s attributes. This is particularly useful in APIs where resources have many attributes, and only a few must be updated frequently.
Choosing Between PUT and PATCH in ASP.NET Core Web API

When deciding whether to use PUT or PATCH in your ASP.NET Core Web API, consider the nature of the updates you expect to perform:

  • If your clients will update entire resources and you can easily provide or require all the data for a resource, PUT is a straightforward choice.
  • If your updates are typically partial or you want to minimize the amount of data that needs to be sent and processed for an update, PATCH is more appropriate.

In the next article, I will discuss How to Implement the HTTP DELETE Method in ASP.NET Core Web API Application with Examples. In this article, I explain How to Implement the HTTP PATCH Method in ASP.NET Core Web API with Examples. I hope you enjoy this article, “How to Implement HTTP PATCH Method in ASP.NET Core Web API.”

Leave a Reply

Your email address will not be published. Required fields are marked *