ASP.NET Core MVC API: How to perform a partial update using HTTP PATCH verb

Published: Thursday 25 June 2020

If you are familiar with API integration, you will be familiar with the CRUD methods that tend to associate themselves with each API controller. You will also be familiar with the HTTP verbs that get used for each method.

The Methods and Verbs

However, if you are new to API integration, or just want a recap, here are the methods and verbs that are used within APIs.

Method HTTP Verb
Read GET
Create POST
Full Update PUT
Partial Update PATCH
Delete DELETE

Partial or Full Update?

We are going to concentrate on when you update, should you do a full update or a partial update?

Picture this scenario. You work for a video game shop. The publishers of a game have officially announced the title of a popular game. You load up the video game record onto your system, which includes the title, publisher and release date.

However, you want to do some additional research on the internet to make sure you have correct title for that video game. Meanwhile, a co-worker of yours has a spreadsheet to update the release dates of a certain number of video games. That spreadsheet includes your game! They go ahead and import the spreadsheet. This updates the release date for your game.

You have finally found the official title of the game, and go ahead and make the change. But wait! There is a problem. Because you haven't reloaded the record, the release date displayed is the date before the spreadsheet was imported.

In this scenario, if you do a full update, it will revert the release date back to it's original value and will hold the incorrect release date.

However, you could do a partial update, only updating the fields that have changed. In this instance, because we have only changed the title of the video game, it will only update the title. All other fields will be left alone.

How to Partially Update in ASP.NET Core API

This assumes you already have an ASP.NET Core API application created. If you don't, go ahead and create one in Visual Studio.

Create a ASP.NET Core API application in Visual Studio

Create a ASP.NET Core API application in Visual Studio

It also assumes you are using ASP.NET Core 3.1. Older or new versions of ASP.NET Core may differ.

Next, you will have to make sure that you include the following NuGet packages are included in your application.

  • Microsoft.AspNetCore.JsonPatch
  • Microsoft.AspNetCore.Mvc.NewtonsoftJson

These packages allow us to perform a HTTP PATCH method properly in ASP.NET Core application.

Changes to Startup

Open up your Startup class. Inside your ConfigureServices method, you should have the following:

// Startup.cs
services.AddControllers();

You need to replace with the following line to integrate Newtonsoft Json into your controllers:

// Startup.cs
services.AddControllers().AddNewtonsoftJson();

The Controller and Entity

For this demonstration, we have created a VideoGame class. This will hold information, such as Id, Title, Publisher and Release Date.

// VideoGame.cs
public partial class VideoGame
{
	public virtual int Id { get; set; }

	public virtual string Title { get; set; }

	public virtual string Publisher { get; set; }

	public virtual DateTime? ReleaseDate { get; set; }

	public VideoGame(int id, string title, string publisher, DateTime? releaseDate)
	{
		Id = id;
		Title = title;
		Publisher = publisher;
		ReleaseDate = releaseDate;
	}
}

Afterwards, we create a Video Game controller. When initalised, we create a list of all the video games we want to store:

// VideoGameController.cs
[Route("api/video-game")]
public class VideoGameController : Controller
{
	IList<VideoGame> VideoGames { get; set; }

	public VideoGameController()
	{
		VideoGames = new List<VideoGame>();

		VideoGames.Add(new VideoGame(1, "Call of Duty: Warzone", "Activision", new System.DateTime(2020, 3, 10)));
		VideoGames.Add(new VideoGame(2, "Friday the 13th: The Game", "Gun Media", new System.DateTime(2017, 5, 26)));
		VideoGames.Add(new VideoGame(3, "DOOM Eternal", "Bethesda", new System.DateTime(2020, 3, 20)));
	}
}

Creating the PATCH method

Now time to go ahead and create the PATCH method. For the PATCH method, we pass in the Id of the record we wish to update. In addition, we pass in an additional parameter of type JsonPatchDocument which includes a generic class. The generic class will be the class looking to update. In this instance, it will be of type "VideoGame". Remember to include the "[FromBody]" attribute before it.

// VideoGameController.cs
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<VideoGame> patchEntity)

Afterwards, we do a look up of the record against the Id and store it in a variable. If the record does not exist, we throw a 404 not found error.

However, if it is found, we apply the changes from our "patch" entity to our record. This means that we only update properties in our record that are contained in our "patch" entity.

// VideoGameController.cs
[HttpPatch("{id:int}")]
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<VideoGame> patchEntity)
{
	var entity = VideoGames.FirstOrDefault(videoGame => videoGame.Id == id);

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

	patchEntity.ApplyTo(entity, ModelState); // Must have Microsoft.AspNetCore.Mvc.NewtonsoftJson installed

	return Ok(entity);
}

We then return the record from our method to verify that the properties that we have changes are the only ones that have updated.

Here is our updated VideoGameController class.

// VideoGameController.cs
[Route("api/video-game")]
public class VideoGameController : Controller
{
	IList<VideoGame> VideoGames { get; set; }

	public VideoGameController()
	{
		VideoGames = new List<VideoGame>();

		VideoGames.Add(new VideoGame(1, "Call of Duty: Warzone", "Activision", new System.DateTime(2020, 3, 10)));
		VideoGames.Add(new VideoGame(2, "Friday the 13th: The Game", "Gun Media", new System.DateTime(2017, 5, 26)));
		VideoGames.Add(new VideoGame(3, "DOOM Eternal", "Bethesda", new System.DateTime(2020, 3, 20)));
	}

	[HttpPatch("{id:int}")]
	public IActionResult Patch(int id, [FromBody] JsonPatchDocument<VideoGame> patchEntity)
	{
		var entity = VideoGames.FirstOrDefault(videoGame => videoGame.Id == id);

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

		patchEntity.ApplyTo(entity, ModelState); // Must have Microsoft.AspNetCore.Mvc.NewtonsoftJson installed
		
		return Ok(entity);
	}
}

How to Test the PATCH Method?

To do this, you can useĀ Postman. In this example, we are going to update the title of "Friday the 13th: The Game" to "Friday the 13th". Create a new request with the following properties:

  • Endpoint: [YOUR_API_HOST]/api/video-game/2
  • HTTP Verb: PATCH
  • Content-Type: application/json

We then need to pass in a JSON formatted body, like the following:

[
	{
		"value": "Friday the 13th",
		"path": "/title",
		"op": "replace"
	}
]
Using Postman to perform a HTTP PATCH update

Using Postman to perform a HTTP PATCH update

The "path" property represents the name of the property. So, "/title" represents the "Title" property in the VideoGame class.

And that's it! The entity returned should have all the information for that particular game, but with only the title updated.

Return a HTTP PATCH entity in Postman

Return a HTTP PATCH entity in Postman

If you wish to update more then one field, you just need to specify an additional line when providing your body. In this instance we are updating the Title and Release Date in one API call:

[
	{
		"value": "Friday the 13th",
		"path": "/Title",
		"op": "replace"
	},
	{
		"value": "2020-04-01",
		"path": "/releaseDate",
		"op": "replace"
	}    
]

Testing In Postman

We have created an ASP.NET Core MVC API application to test our method. Once the application is running, we open up Postman to check that only the properties that we have changed have been updated.

Reduces Risk

Doing a partial update reduces the risk of data being overwritten. It doesn't eliminate it because you could have updated the release date when updating the title. To stop that from happening, you would have to introduce some sort of system where you lock a record. But it certainly shows some of the benefits of doing a partial update and how to integrate it easily into your ASP.NET Core API.