{"id":1122,"date":"2025-05-27T14:46:47","date_gmt":"2025-05-27T06:46:47","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1122"},"modified":"2025-05-27T14:46:47","modified_gmt":"2025-05-27T06:46:47","slug":"ultimate-asp-net-core-web-api-12-working-with-patch-requests","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1122","title":{"rendered":"Ultimate ASP.NET Core Web API 12 WORKING WITH PATCH REQUESTS"},"content":{"rendered":"<h1>12 WORKING WITH PATCH REQUESTS<\/h1>\n<p>12 \u4f7f\u7528PATCH\u8bf7\u6c42<\/p>\n<p>In the previous chapter, we worked with the PUT request to fully update our resource. But if we want to update our resource only partially, we should use PATCH.\u200c<br \/>\n\u5728\u4e0a\u4e00\u7ae0\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u4e86 PUT \u8bf7\u6c42\u6765\u5b8c\u5168\u66f4\u65b0\u6211\u4eec\u7684\u8d44\u6e90\u3002\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u60f3\u53ea\u90e8\u5206\u66f4\u65b0\u6211\u4eec\u7684\u8d44\u6e90\uff0c\u6211\u4eec\u5e94\u8be5\u4f7f\u7528 PATCH\u3002<\/p>\n<p>The partial update isn\u2019t the only difference between PATCH and PUT. The request body is different as well. For the Company PATCH request, for example, we should use <code>[FromBody]JsonPatchDocument&lt;Company&gt;<\/code> and not [FromBody]Company as we did with the PUT requests.<br \/>\n\u90e8\u5206\u66f4\u65b0\u5e76\u4e0d\u662f PATCH \u548c PUT \u4e4b\u95f4\u7684\u552f\u4e00\u533a\u522b\u3002\u8bf7\u6c42\u6b63\u6587\u4e5f\u4e0d\u540c\u3002\u4f8b\u5982\uff0c\u5bf9\u4e8e Company PATCH \u8bf7\u6c42\uff0c\u6211\u4eec\u5e94\u8be5\u4f7f\u7528 <code>[FromBody]JsonPatchDocument&lt;Company&gt;<\/code>\uff0c\u800c\u4e0d\u662f [FromBody]Company\uff0c\u5c31\u50cf\u6211\u4eec\u5bf9 PUT \u8bf7\u6c42\u6240\u505a\u7684\u90a3\u6837\u3002<\/p>\n<p>Additionally, for the PUT request\u2019s media type, we have used application\/json \u2014 but for the PATCH request\u2019s media type, we should use application\/json-patch+json. Even though the first one would be accepted in ASP.NET Core for the PATCH request, the recommendation by REST standards is to use the second one.<br \/>\n\u6b64\u5916\uff0c\u5bf9\u4e8e PUT \u8bf7\u6c42\u7684\u5a92\u4f53\u7c7b\u578b\uff0c\u6211\u4eec\u4f7f\u7528\u4e86 application\/json\uff0c\u4f46\u5bf9\u4e8e PATCH \u8bf7\u6c42\u7684\u5a92\u4f53\u7c7b\u578b\uff0c\u6211\u4eec\u5e94\u8be5\u4f7f\u7528 application\/json-patch+json\u3002\u5c3d\u7ba1 PATCH \u8bf7\u6c42\u7684 ASP.NET Core \u4f1a\u63a5\u53d7\u7b2c\u4e00\u4e2a\u9009\u9879\uff0c\u4f46 REST \u6807\u51c6\u5efa\u8bae\u4f7f\u7528\u7b2c\u4e8c\u4e2a\u9009\u9879\u3002<\/p>\n<p>Let\u2019s see what the PATCH request body looks like:<br \/>\n\u8ba9\u6211\u4eec\u770b\u770b PATCH \u8bf7\u6c42\u6b63\u6587\u662f\u4ec0\u4e48\u6837\u5b50\u7684\uff1a<\/p>\n<pre><code>[ { &quot;op&quot;: &quot;replace&quot;, &quot;path&quot;: &quot;\/name&quot;, &quot;value&quot;: &quot;new name&quot; }, { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;\/name&quot; } ]<\/code><\/pre>\n<p>The square brackets represent an array of operations. Every operation is placed between curly brackets. So, in this specific example, we have two operations: Replace and Remove represented by the op property. The path property represents the object\u2019s property that we want to modify and the value property represents a new value.<br \/>\n\u65b9\u62ec\u53f7\u8868\u793a\u4e00\u7ec4\u4f5c\u3002\u6bcf\u4e2a\u4f5c\u90fd\u653e\u5728\u5927\u62ec\u53f7\u4e4b\u95f4\u3002\u56e0\u6b64\uff0c\u5728\u8fd9\u4e2a\u7279\u5b9a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u6709\u4e24\u4e2a\u4f5c\uff1a\u7531 op \u5c5e\u6027\u8868\u793a\u7684 Replace \u548c Remove\u3002path \u5c5e\u6027\u8868\u793a\u6211\u4eec\u8981\u4fee\u6539\u7684\u5bf9\u8c61\u5c5e\u6027\uff0cvalue \u5c5e\u6027\u8868\u793a\u65b0\u503c\u3002<\/p>\n<p>In this specific example, for the first operation, we replace the value of the name property with a new name. In the second example, we remove the name property, thus setting its value to default.<br \/>\n\u5728\u8fd9\u4e2a\u7279\u5b9a\u793a\u4f8b\u4e2d\uff0c\u5bf9\u4e8e\u7b2c\u4e00\u4e2a\u4f5c\uff0c\u6211\u4eec\u5c06 name \u5c5e\u6027\u7684\u503c\u66ff\u6362\u4e3a\u65b0\u540d\u79f0\u3002\u5728\u7b2c\u4e8c\u4e2a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u5220\u9664\u4e86 name \u5c5e\u6027\uff0c\u4ece\u800c\u5c06\u5176\u503c\u8bbe\u7f6e\u4e3a default\u3002<\/p>\n<p>There are six different operations for a PATCH request:<br \/>\nPATCH \u8bf7\u6c42\u6709 6 \u79cd\u4e0d\u540c\u7684\u64cd\u4f5c\u4f5c\uff1a<\/p>\n<table>\n<thead>\n<tr>\n<th>OPERATION<\/th>\n<th>REQUEST BODY<\/th>\n<th>EXPLANATION<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Add<\/td>\n<td>{ &quot;op&quot;: &quot;add&quot;, &quot;path&quot;: &quot;\/name&quot;, &quot;value&quot;: &quot;new value&quot; }<\/td>\n<td>Assigns a new value to a required property.<\/td>\n<\/tr>\n<tr>\n<td>Remove<\/td>\n<td>{ &quot;op&quot;: &quot;remove&quot;,&quot;path&quot;: &quot;\/name&quot;}<\/td>\n<td>Sets a default value to a required property.<\/td>\n<\/tr>\n<tr>\n<td>Replace<\/td>\n<td>{ &quot;op&quot;: &quot;replace&quot;, &quot;path&quot;: &quot;\/name&quot;, &quot;value&quot;: &quot;new value&quot; }<\/td>\n<td>Replaces a value of a required property to a new value.<\/td>\n<\/tr>\n<tr>\n<td>Copy<\/td>\n<td>{&quot;op&quot;: &quot;copy&quot;,&quot;from&quot;: &quot;\/name&quot;,&quot;path&quot;: &quot;\/title&quot;}<\/td>\n<td>Copies the value from a property in the \u201cfrom\u201d part to the property in the \u201cpath\u201d part.<\/td>\n<\/tr>\n<tr>\n<td>Move<\/td>\n<td>{ &quot;op&quot;: &quot;move&quot;, &quot;from&quot;: &quot;\/name&quot;, &quot;path&quot;: &quot;\/title&quot; }<\/td>\n<td>Moves the value from a property in the \u201cfrom\u201d part to a property in the \u201cpath\u201d part.<\/td>\n<\/tr>\n<tr>\n<td>Test<\/td>\n<td>{&quot;op&quot;: &quot;test&quot;,&quot;path&quot;: &quot;\/name&quot;,&quot;value&quot;: &quot;new value&quot;}<\/td>\n<td>Tests if a property has a specified value.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After all this theory, we are ready to dive into the coding part.<br \/>\n\u5728\u6240\u6709\u8fd9\u4e9b\u7406\u8bba\u4e4b\u540e\uff0c\u6211\u4eec\u51c6\u5907\u6df1\u5165\u7814\u7a76\u7f16\u7801\u90e8\u5206\u3002<\/p>\n<h2>12.1 Applying PATCH to the Employee Entity<\/h2>\n<p>12.1 \u5c06 PATCH \u5e94\u7528\u4e8e Employee \u5b9e\u4f53<\/p>\n<p>Before we start with the code modification, we have to install two required libraries:\u200c<br \/>\n\u5728\u6211\u4eec\u5f00\u59cb\u4fee\u6539\u4ee3\u7801\u4e4b\u524d\uff0c\u6211\u4eec\u5fc5\u987b\u5b89\u88c5\u4e24\u4e2a\u5fc5\u9700\u7684\u5e93\uff1a<\/p>\n<p>\u2022 The Microsoft.AspNetCore.JsonPatch library, in the Presentation project, to support the usage of JsonPatchDocument in our controller and<br \/>\nPresentation\u9879\u76ee\u4e2d\u7684 Microsoft.AspNetCore.JsonPatch \u5e93\uff0c\u7528\u4e8e\u652f\u6301\u5728\u63a7\u5236\u5668\u4e2d\u4f7f\u7528 JsonPatchDocument \u548c<\/p>\n<p>\u2022 The Microsoft.AspNetCore.Mvc.NewtonsoftJson library, in the main project, to support request body conversion to a PatchDocument once we send our request.<br \/>\n\u4e3b\u9879\u76ee\u4e2d\u7684 Microsoft.AspNetCore.Mvc.NewtonsoftJson \u5e93\uff0c\u7528\u4e8e\u652f\u6301\u5728\u53d1\u9001\u8bf7\u6c42\u540e\u5c06\u8bf7\u6c42\u6b63\u6587\u8f6c\u6362\u4e3a PatchDocument\u3002<\/p>\n<p>As you can see, we are still using the NewtonsoftJson library to support the PatchDocument conversion. The official statement from Microsoft is that they are not going to replace it with System.Text.Json: \u201cThe main reason is that this will require a huge investment from us, with not a very high value-add for the majority of our customers.\u201d.<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6211\u4eec\u4ecd\u5728\u4f7f\u7528 NewtonsoftJson \u5e93\u6765\u652f\u6301 PatchDocument \u8f6c\u6362\u3002Microsoft \u7684\u5b98\u65b9\u58f0\u660e\u662f\uff0c\u4ed6\u4eec\u4e0d\u4f1a\u7528 System.Text.Json \u66ff\u6362\u5b83\uff1a\u201c\u4e3b\u8981\u539f\u56e0\u662f\u8fd9\u9700\u8981\u6211\u4eec\u8fdb\u884c\u5927\u91cf\u6295\u8d44\uff0c\u5bf9\u4e8e\u6211\u4eec\u7684\u5927\u591a\u6570\u5ba2\u6237\u6765\u8bf4\uff0c\u9644\u52a0\u503c\u4e0d\u662f\u5f88\u9ad8\u3002<\/p>\n<p>By using AddNewtonsoftJson, we are replacing the System.Text.Json formatters for all JSON content. We don\u2019t want to do that so, we are going ton add a simple workaround in the Program class:<br \/>\n\u901a\u8fc7\u4f7f\u7528 AddNewtonsoftJson\uff0c\u6211\u4eec\u5c06\u66ff\u6362\u6240\u6709 JSON \u5185\u5bb9\u7684 System.Text.Json \u683c\u5f0f\u5316\u7a0b\u5e8f\u3002\u6211\u4eec\u4e0d\u60f3\u8fd9\u6837\u505a\uff0c\u56e0\u6b64\uff0c\u6211\u4eec\u5c06\u5728 Program \u7c7b\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u7b80\u5355\u7684\u89e3\u51b3\u65b9\u6cd5\uff1a<\/p>\n<pre><code>NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter() =&gt; \n    new ServiceCollection()\n    .AddLogging()\n    .AddMvc()\n    .AddNewtonsoftJson()\n    .Services.BuildServiceProvider()\n    .GetRequiredService&lt;IOptions&lt;MvcOptions&gt;&gt;()\n    .Value.InputFormatters.OfType&lt;NewtonsoftJsonPatchInputFormatter&gt;()\n    .First();<\/code><\/pre>\n<p>By adding a method like this in the Program class, we are creating a local function. This function configures support for JSON Patch using Newtonsoft.Json while leaving the other formatters unchanged.<br \/>\n\u901a\u8fc7\u5728 Program \u7c7b\u4e2d\u6dfb\u52a0\u8fd9\u6837\u7684\u65b9\u6cd5\uff0c\u6211\u4eec\u6b63\u5728\u521b\u5efa\u4e00\u4e2a\u672c\u5730\u51fd\u6570\u3002\u6b64\u51fd\u6570\u4f7f\u7528 Newtonsoft.Json \u914d\u7f6e\u5bf9 JSON Patch \u7684\u652f\u6301\uff0c\u540c\u65f6\u4fdd\u6301\u5176\u4ed6\u683c\u5f0f\u5316\u7a0b\u5e8f\u4e0d\u53d8\u3002<\/p>\n<p>For this to work, we have to include two more namespaces in the class:<br \/>\n\u4e3a\u6b64\uff0c\u6211\u4eec\u5fc5\u987b\u5728 class \u4e2d\u518d\u5305\u542b\u4e24\u4e2a\u547d\u540d\u7a7a\u95f4\uff1a<\/p>\n<pre><code>using Microsoft.AspNetCore.Mvc.Formatters; \nusing Microsoft.Extensions.Options;<\/code><\/pre>\n<p>After that, we have to modify the AddControllers method:<br \/>\n\u4e4b\u540e\uff0c\u6211\u4eec\u5fc5\u987b\u4fee\u6539 AddControllers \u65b9\u6cd5\uff1a<\/p>\n<pre><code>builder.Services.AddControllers(config =&gt; { \n    config.RespectBrowserAcceptHeader = true; \n    config.ReturnHttpNotAcceptable = true; \n    config.InputFormatters.Insert(0, GetJsonPatchInputFormatter()); \n}).AddXmlDataContractSerializerFormatters()\n.AddCustomCSVFormatter()\n.AddApplicationPart(typeof(CompanyEmployees.Presentation.AssemblyReference)\n.Assembly);\n\n\/\/ config.InputFormatters.Insert(0, GetJsonPatchInputFormatter());<\/code><\/pre>\n<p>We are placing our JsonPatchInputFormatter at the index 0 in the InputFormatters list.<br \/>\n\u6211\u4eec\u5c06 JsonPatchInputFormatter \u653e\u5728 InputFormatters \u5217\u8868\u4e2d\u7684\u7d22\u5f15 0 \u5904\u3002<\/p>\n<p>We will require a mapping from the Employee type to the EmployeeForUpdateDto type. Therefore, we have to create a mapping rule for that.<br \/>\n\u6211\u4eec\u9700\u8981\u4ece Employee \u7c7b\u578b\u5230 EmployeeForUpdateDto \u7c7b\u578b\u7684\u6620\u5c04\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u5fc5\u987b\u4e3a\u6b64\u521b\u5efa\u4e00\u4e2a\u6620\u5c04\u89c4\u5219\u3002<\/p>\n<p>If we take a look at the MappingProfile class, we will see that we have a mapping from the EmployeeForUpdateDto to the Employee type:<br \/>\n\u5982\u679c\u6211\u4eec\u770b\u4e00\u4e0b MappingProfile \u7c7b\uff0c\u6211\u4eec\u5c06\u770b\u5230\u6211\u4eec\u6709\u4e00\u4e2a\u4ece EmployeeForUpdateDto \u5230 Employee \u7c7b\u578b\u7684\u6620\u5c04\uff1a<\/p>\n<pre><code>CreateMap&lt;EmployeeForUpdateDto, Employee&gt;();<\/code><\/pre>\n<p>But we need it another way. To do so, we are not going to create an additional rule; we can just use the ReverseMap method to help us in the process:<br \/>\n\u4f46\u6211\u4eec\u9700\u8981\u53e6\u4e00\u79cd\u65b9\u5f0f\u3002\u4e3a\u6b64\uff0c\u6211\u4eec\u4e0d\u4f1a\u521b\u5efa\u5176\u4ed6\u89c4\u5219;\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 ReverseMap \u65b9\u6cd5\u6765\u5728\u6b64\u8fc7\u7a0b\u4e2d\u5e2e\u52a9\u6211\u4eec\uff1a<\/p>\n<pre><code>CreateMap&lt;EmployeeForUpdateDto, Employee&gt;().ReverseMap();<\/code><\/pre>\n<p>The ReverseMap method is also going to configure this rule to execute reverse mapping if we ask for it.<br \/>\nReverseMap \u65b9\u6cd5\u8fd8\u5c06\u914d\u7f6e\u6b64\u89c4\u5219\uff0c\u4ee5\u4fbf\u5728\u6211\u4eec\u8981\u6c42\u65f6\u6267\u884c\u53cd\u5411\u6620\u5c04\u3002<\/p>\n<p>After that, we are going to add two new method contracts to the IEmployeeService interface:<br \/>\n\u4e4b\u540e\uff0c\u6211\u4eec\u5c06\u5411 IEmployeeService \u63a5\u53e3\u6dfb\u52a0\u4e24\u4e2a\u65b0\u7684\u65b9\u6cd5\u534f\u5b9a\uff1a<\/p>\n<pre><code>using Entities.Models;\nusing Shared.DataTransferObjects;\n\nnamespace Service.Contracts\n{\n    public interface IEmployeeService\n    {\n        IEnumerable&lt;EmployeeDto&gt; GetEmployees(Guid companyId, bool trackChanges);\n        EmployeeDto GetEmployee(Guid companyId, Guid id, bool trackChanges);\n        EmployeeDto CreateEmployeeForCompany(Guid companyId, EmployeeForCreationDto employeeForCreation, bool trackChanges);\n        void DeleteEmployeeForCompany(Guid companyId, Guid id, bool trackChanges);\n        void UpdateEmployeeForCompany(Guid companyId, Guid id, EmployeeForUpdateDto employeeForUpdate, bool compTrackChanges, bool empTrackChanges);\n        (EmployeeForUpdateDto employeeToPatch, Employee employeeEntity) GetEmployeeForPatch(Guid companyId, Guid id, bool compTrackChanges, bool empTrackChanges); \n        void SaveChangesForPatch(EmployeeForUpdateDto employeeToPatch, Employee employeeEntity);\n    }\n}<\/code><\/pre>\n<p>Of course, for this to work, we have to add the reference to the Entities project.<br \/>\n\u5f53\u7136\uff0c\u8981\u4f7f\u5176\u6b63\u5e38\u5de5\u4f5c\uff0c\u6211\u4eec\u5fc5\u987b\u6dfb\u52a0\u5bf9 Entities \u9879\u76ee\u7684\u5f15\u7528\u3002<\/p>\n<p>Then, we have to implement these two methods in the EmployeeService class:<br \/>\n\u7136\u540e\uff0c\u6211\u4eec\u5fc5\u987b\u5728 EmployeeService \u7c7b\u4e2d\u5b9e\u73b0\u8fd9\u4e24\u4e2a\u65b9\u6cd5\uff1a<\/p>\n<pre><code>public (EmployeeForUpdateDto employeeToPatch, Employee employeeEntity) GetEmployeeForPatch(Guid companyId, Guid id, bool compTrackChanges, bool empTrackChanges)\n{\n    var company = _repository.Company.GetCompany(companyId, compTrackChanges);\n    if (company is null)\n        throw new CompanyNotFoundException(companyId);\n    var employeeEntity = _repository.Employee.GetEmployee(companyId, id, empTrackChanges);\n    if (employeeEntity is null)\n        throw new EmployeeNotFoundException(companyId);\n    var employeeToPatch = _mapper.Map&lt;EmployeeForUpdateDto&gt;(employeeEntity);\n    return (employeeToPatch, employeeEntity);\n}\npublic void SaveChangesForPatch(EmployeeForUpdateDto employeeToPatch, Employee employeeEntity)\n{\n    _mapper.Map(employeeToPatch, employeeEntity);\n    _repository.Save();\n}<\/code><\/pre>\n<p>In the first method, we are trying to fetch both the company and employee from the database and if we can\u2019t find either of them, we stop the execution flow and return the NotFound response to the client. Then, we map the employee entity to the EmployeeForUpdateDto type and return both objects (employeeToPatch and employeeEntity) inside the Tuple to the controller.<br \/>\n\u5728\u7b2c\u4e00\u79cd\u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u5c1d\u8bd5\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u516c\u53f8\u548c\u5458\u5de5\uff0c\u5982\u679c\u627e\u4e0d\u5230\u4ed6\u4eec\u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a\uff0c\u6211\u4eec\u5c06\u505c\u6b62\u6267\u884c\u6d41\u5e76\u5c06 NotFound \u54cd\u5e94\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\u3002\u7136\u540e\uff0c\u6211\u4eec\u5c06 employee \u5b9e\u4f53\u6620\u5c04\u5230 EmployeeForUpdateDto \u7c7b\u578b\uff0c\u5e76\u5c06 Tuple \u4e2d\u7684\u4e24\u4e2a\u5bf9\u8c61\uff08employeeToPatch \u548c employeeEntity\uff09\u8fd4\u56de\u7ed9\u63a7\u5236\u5668\u3002<\/p>\n<p>The second method just maps from emplyeeToPatch to employeeEntity and calls the repository's Save method.<br \/>\n\u7b2c\u4e8c\u4e2a\u65b9\u6cd5\u53ea\u662f\u4ece emplyeeToPatch \u6620\u5c04\u5230 employeeEntity\uff0c\u5e76\u8c03\u7528\u5b58\u50a8\u5e93\u7684 Save \u65b9\u6cd5\u3002<\/p>\n<p>Now, we can modify our controller:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u4fee\u6539\u6211\u4eec\u7684\u63a7\u5236\u5668\uff1a<\/p>\n<pre><code>[HttpPatch(&quot;{id:guid}&quot;)]\npublic IActionResult PartiallyUpdateEmployeeForCompany(Guid companyId, Guid id, [FromBody] JsonPatchDocument&lt;EmployeeForUpdateDto&gt; patchDoc)\n{\n    if (patchDoc is null)\n        return BadRequest(&quot;patchDoc object sent from client is null.&quot;);\n    var result = _service.EmployeeService.GetEmployeeForPatch(companyId, id, compTrackChanges: false, empTrackChanges: true);\n    patchDoc.ApplyTo(result.employeeToPatch);\n    _service.EmployeeService.SaveChangesForPatch(result.employeeToPatch, result.employeeEntity);\n    return NoContent();\n}<\/code><\/pre>\n<p>You can see that our action signature is different from the PUT actions. We are accepting the JsonPatchDocument from the request body. After that, we have a familiar code where we check the patchDoc for null value and if it is, we return a BadRequest. Then we call the service method where we map from the Employee type to the EmployeeForUpdateDto type; we need to do that because the patchDoc variable can apply only to the EmployeeForUpdateDto type. After apply is executed, we call another service method to map again to the Employee type (from employeeToPatch to employeeEntity) and save changes in the database. In the end, we return NoContent.<br \/>\n\u60a8\u53ef\u4ee5\u770b\u5230\u6211\u4eec\u7684\u4f5c\u7b7e\u540d\u4e0e PUT\u4f5c\u4e0d\u540c\u3002\u6211\u4eec\u6b63\u5728\u63a5\u53d7\u6765\u81ea\u8bf7\u6c42\u6b63\u6587\u7684 JsonPatchDocument\u3002\u4e4b\u540e\uff0c\u6211\u4eec\u6709\u4e00\u4e2a\u719f\u6089\u7684\u4ee3\u7801\uff0c\u6211\u4eec\u5728\u5176\u4e2d\u68c0\u67e5 patchDoc \u662f\u5426\u4e3a null \u503c\uff0c\u5982\u679c\u4e3a\u7a7a\uff0c\u5219\u8fd4\u56de\u4e00\u4e2a BadRequest\u3002\u7136\u540e\uff0c\u6211\u4eec\u8c03\u7528\u670d\u52a1\u65b9\u6cd5\uff0c\u4ece\u5176\u4e2d Employee \u7c7b\u578b\u6620\u5c04\u5230 EmployeeForUpdateDto \u7c7b\u578b;\u6211\u4eec\u9700\u8981\u8fd9\u6837\u505a\uff0c\u56e0\u4e3a patchDoc \u53d8\u91cf\u53ea\u80fd\u5e94\u7528\u4e8e EmployeeForUpdateDto \u7c7b\u578b\u3002\u6267\u884c apply \u540e\uff0c\u6211\u4eec\u8c03\u7528\u53e6\u4e00\u4e2a\u670d\u52a1\u65b9\u6cd5\u518d\u6b21\u6620\u5c04\u5230 Employee \u7c7b\u578b\uff08\u4ece employeeToPatch \u5230 employeeEntity\uff09\u5e76\u5c06\u66f4\u6539\u4fdd\u5b58\u5728\u6570\u636e\u5e93\u4e2d\u3002\u6700\u540e\uff0c\u6211\u4eec\u8fd4\u56de NoContent\u3002<\/p>\n<p>Don\u2019t forget to include an additional namespace:<br \/>\n\u4e0d\u8981\u5fd8\u8bb0\u5305\u542b\u989d\u5916\u7684\u547d\u540d\u7a7a\u95f4\uff1a<\/p>\n<pre><code>using Microsoft.AspNetCore.JsonPatch;<\/code><\/pre>\n<p>Now, we can send a couple of requests to test this code:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u53d1\u9001\u51e0\u4e2a\u8bf7\u6c42\u6765\u6d4b\u8bd5\u6b64\u4ee3\u7801\uff1a<\/p>\n<p>Let\u2019s first send the replace operation:<br \/>\n\u8ba9\u6211\u4eec\u9996\u5148\u53d1\u9001 replace\u4f5c\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1201.jpg\" alt=\"alt text\" \/><\/p>\n<p>It works; we get the 204 No Content message. Let\u2019s check the same employee:<br \/>\n\u5b83\u6709\u6548;\u6211\u4eec\u6536\u5230 204 No Content \u6d88\u606f\u3002\u8ba9\u6211\u4eec\u68c0\u67e5\u540c\u4e00\u540d\u5458\u5de5\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1202.jpg\" alt=\"alt text\" \/><\/p>\n<p>And we see the Age property has been changed.<br \/>\n\u6211\u4eec\u770b\u5230 Age \u5c5e\u6027\u5df2\u66f4\u6539\u3002<\/p>\n<p>Let\u2019s send a remove operation in a request:<br \/>\n\u8ba9\u6211\u4eec\u5728\u8bf7\u6c42\u4e2d\u53d1\u9001\u4e00\u4e2a remove\u4f5c\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1203.jpg\" alt=\"alt text\" \/><\/p>\n<p>This works as well. Now, if we check our employee, its age is going to be set to 0 (the default value for the int type):<br \/>\n\u8fd9\u4e5f\u6709\u6548\u3002\u73b0\u5728\uff0c\u5982\u679c\u6211\u4eec\u68c0\u67e5\u6211\u4eec\u7684\u5458\u5de5\uff0c\u5b83\u7684 age \u5c06\u88ab\u8bbe\u7f6e\u4e3a 0\uff08int \u7c7b\u578b\u7684\u9ed8\u8ba4\u503c\uff09\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1204.jpg\" alt=\"alt text\" \/><\/p>\n<p>Finally, let\u2019s return a value of 28 for the Age property:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4e3a Age \u5c5e\u6027\u8fd4\u56de\u503c 28\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees\/80ABBCA8-664D-4B20-B5DE-024705497D4A<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1205.jpg\" alt=\"alt text\" \/><\/p>\n<p>Let\u2019s check the employee now:<br \/>\n\u73b0\u5728\u8ba9\u6211\u4eec\u68c0\u67e5\u4e00\u4e0b\u5458\u5de5\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1206.jpg\" alt=\"alt text\" \/><\/p>\n<p>Excellent.<br \/>\n\u975e\u5e38\u597d\u3002<\/p>\n<p>Everything works as expected.<br \/>\n\u4e00\u5207\u90fd\u6309\u9884\u671f\u8fdb\u884c\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>12 WORKING WITH PATCH REQUESTS 12 \u4f7f\u7528PATCH\u8bf7\u6c42 In the previous chapter, we worked with the PUT request to fully update our resource. But if we want to update our resource only partially, we should use PATCH.\u200c \u5728\u4e0a\u4e00\u7ae0\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u4e86 PUT \u8bf7\u6c42\u6765\u5b8c\u5168\u66f4\u65b0\u6211\u4eec\u7684\u8d44\u6e90\u3002\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u60f3\u53ea\u90e8\u5206\u66f4\u65b0\u6211\u4eec\u7684\u8d44\u6e90\uff0c\u6211\u4eec\u5e94\u8be5\u4f7f\u7528 PATCH\u3002 The partial update isn\u2019t the only difference between PATCH and PUT. The request body is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1122","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1122","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1122"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1122\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}