在前面的两篇文章中,我们讨论了很多关于使用HttpClient进行CRUD操作的基础知识。如果你已经读过它们,你就知道如何使用HttpClient从API中获取数据,并使用HttpClient发送POST、PUT和DELETE请求。当我们使用PUT请求时,用它来更新我们的资源。但我们可以通过使用HTTP PATCH请求进行部分更新来改进这一点。因此,在本文中,我们将展示如何使用HttpClient发送HTTP PATCH请求来实现资源的部分更新,从而提高应用程序的性能。

要下载源码,可以访问https://github.com/CodeMazeBlog/httpclient-aspnetcore/tree/patch-with-httpclient获取项目。

更多关于HTTP PATCH请求

正如我们已经提到的,我们使用PUT请求进行完整更新,使用PATCH请求进行部分更新。但这并不是这两个HTTP请求之间的唯一区别。首先,请求主体是不同的。如果我们检查Web API中的PUT,我们可以看到请求体是一个简单的对象:

[FromBody] CompanyForUpdateDto company

但如果我们对PATCH请求做同样的检查:

[FromBody] JsonPatchDocument<EmployeeForUpdateDto> patchDoc

可以看到,如果我们想要支持PATCH请求的请求体,我们必须使用JsonPatchDocument类。这个类帮助我们描述可以使用PATCH请求执行的不同操作集。

同样,对于PUT请求,我们使用application/json作为媒体类型。但是对于PATCH请求,首选的媒体类型是application/json-patch+json。我们可以为HTTP PATCH请求使用application/json媒体类型,但正如我们提到的,首选的媒体类型是application/json-patch+json,我们将在示例中使用它。

HTTP PATCH操作

PATCH请求可以作为JSON数组的一部分执行一个或多个操作。让我们看看PATCH请求的请求体:

[
{
"op": "replace",
"path": "/name",
"value": "new name"
},
{
"op": "remove",
"path": "/name"
}
]

正如我们所看到的,请求主体基本上是一个指定不同操作的JSON对象数组。也就是说,我们可以确认两个操作:由op属性指定的Replace和Remove。路径部分表示到我们想要修改的对象属性的路径。最后,value部分表示一个新值,我们用它来替换Name属性的旧值。

使用HttpClient的PatchAsync方法发送HTTP PATCH请求

在我们开始修改客户端项目之前,可以快速地看一下API的PATCH操作的路径:

[Route("api/companies/{companyId}/employees")]
[ApiController]
public class EmployeesController : ControllerBase

可以看到我们已经在EmployeesController中实现了PATCH操作。由于单个员工不能在没有单个公司的情况下存在,所以到这个控制器的路由是:

api/companies/{companyId}/employees

但是,我们只更新了一个雇员,所以我们需要该雇员的id:

[HttpPatch("{id}")]
public IActionResult PartiallyUpdateEmployeeForCompany(Guid companyId, Guid id, [FromBody] JsonPatchDocument<EmployeeForUpdateDto> patchDoc)

这意味着该操作的路由是:

api/companies/{companyId}/employees/{id}

为了简单起见,我们已经从这个控制器中删除了其余的操作,另外,为了便于参考,让我们展示一下API实现:

[HttpPatch("{id}")]
public IActionResult PartiallyUpdateEmployeeForCompany(Guid companyId, Guid id, [FromBody] JsonPatchDocument<EmployeeForUpdateDto> patchDoc)
{
if(patchDoc == null)
{
_logger.LogError("patchDoc object sent from client is null.");
return BadRequest("patchDoc object is null");
}
var company = _repository.Company.GetCompany(companyId, trackChanges: false);
if (company == null)
{
_logger.LogInfo($"Company with id: {companyId} doesn't exist in the database.");
return NotFound();
}
var employeeEntity = _repository.Employee.GetEmployee(companyId, id, trackChanges: true);
if (employeeEntity == null)
{
_logger.LogInfo($"Employee with id: {id} doesn't exist in the database.");
return NotFound();
}
var employeeToPatch = _mapper.Map<EmployeeForUpdateDto>(employeeEntity);
patchDoc.ApplyTo(employeeToPatch);
_mapper.Map(employeeToPatch, employeeEntity);
_repository.Save();
return NoContent();
}

从请求体接受JsonPatchDocument对象。接下来,我们检查patchDoc对象是否存在空值,以及公司和员工是否存在于数据库中。然后,我们把Employee类型映射到EmployeeForUpdateDto类型。这对我们来说很重要,因为patchDoc对象只能应用于EmployeeForUpdateDto类型。在调用ApplyTo方法之后,我们将再次映射到员工类型,并将更改保存到数据库中。

客户端实现

现在,让我们打开客户端项目,并在Services文件夹中添加一个新服务:

public class HttpClientPatchService : IHttpClientServiceImplementation
{
private static readonly HttpClient _httpClient = new HttpClient(); public HttpClientPatchService()
{
_httpClient.BaseAddress = new Uri("https://localhost:5001/api/");
_httpClient.Timeout = new TimeSpan(0, 0, 30);
_httpClient.DefaultRequestHeaders.Clear();
} public async Task Execute()
{
throw new NotImplementedException();
}
}

因此,这是HttpClient类的初始配置。一旦开始学习HttpClientFactory,我们将展示如何能够一次性进行配置,而不会为每个服务重复它。

在此之后,我们可以使用PatchAsync方法实现发送HTTP PATCH请求的逻辑:

private async Task PatchEmployee()
{
var patchDoc = new JsonPatchDocument<EmployeeForUpdateDto>();
patchDoc.Replace(e => e.Name, "Sam Raiden Updated");
patchDoc.Remove(e => e.Age); var uri = Path.Combine("companies", "C9D4C053-49B6-410C-BC78-2D54A9991870", "employees", "80ABBCA8-664D-4B20-B5DE-024705497D4A");
var serializedDoc = JsonConvert.SerializeObject(patchDoc);
var requestContent = new StringContent(serializedDoc, Encoding.UTF8, "application/json-patch+json"); var response = await _httpClient.PatchAsync(uri, requestContent);
response.EnsureSuccessStatusCode();
}

在这里,我们在JsonPatchDocument类的帮助下创建了一个新的Patch类。为了能够使用这个类,我们必须安装Microsoft.AspNetCore.JsonPatch。接下来,我们使用JsonPatchDocument类中的两个helper方法创建两个操作。然后,创建URI、序列化对象,并创建新的字符串内容。这里需要注意的重要一点是,我们没有使用System.Text中的JsonSerializer.Serialize()方法。而是使用Newtonsoft.Json的JsonConvert.SerializeObject()方法。我们必须这么做,因为PATCH文件不能很好地用System.Text.Json序列化,我们的API会收到400 bad request。

最后,我们使用PatchAsync方法发送请求,并确保响应有一个成功的状态码。

现在,让我们修改Execute方法:

public async Task Execute(){   
  await PatchEmployee();

将服务注册到Program类:

private static void ConfigureServices(IServiceCollection services)
{
//services.AddScoped<IHttpClientServiceImplementation, HttpClientCrudService>();
services.AddScoped<IHttpClientServiceImplementation, HttpClientPatchService>();
}

在PatchEmployee放置断点,并启动项目。

检查数据库:

我们可以看到Name列被修改,Age列被设置为默认值0。让我们看看如何使用HttpRequestMessage来实现同样的功能。

使用HttpRequestMessage发送PATCH请求

与前面处理所有HTTP请求时一样,我们将使用HttpRequestMessage类向服务器送PATCH请求。在本教程的前几篇文章中,我们已经讨论了这种方法的好处。

所以,让我们在HttpClientPatchService类中添加另一个方法:

private async Task PatchEmployeeWithHttpRequestMessage()
{
var patchDoc = new JsonPatchDocument<EmployeeForUpdateDto>();
patchDoc.Replace(e => e.Name, "Sam Raiden");
patchDoc.Add(e => e.Age, 28); var uri = Path.Combine("companies", "C9D4C053-49B6-410C-BC78-2D54A9991870", "employees", "80ABBCA8-664D-4B20-B5DE-024705497D4A");
var serializedDoc = JsonConvert.SerializeObject(patchDoc); var request = new HttpRequestMessage(HttpMethod.Patch, uri);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent(serializedDoc, Encoding.UTF8);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json-patch+json"); var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}

我们再次创建了JsonPatchDocument对象,但这一次,我们replace了员工的姓名,并add了年纪。准备URI并序列化对象。完成之后,我们创建一个新的HttpRequestMessage,提供我们想要使用的HTTP方法和URI。就像我们在所有的HttpRequestMessage示例中所做的那样,我们为请求添加了一个accept header、content和content type。最后,我们使用SendAsync方法发送请求,并确保响应中的状态码成功。

为了能够执行这个方法,我们必须在execute方法中调用它:

public async Task Execute()
{
//await PatchEmployee();
await PatchEmployeeWithHttpRequestMessage();

在方法中放置断点,并启动项目:

检查数据库:

结论

在本文中——包括前面的文章,我们讨论了所有的CRUD请求,包括HTTP PATCH请求。现在我们知道了如何使用HttpClient发送所有这些类型的请求,也使用了HttpRequestMessage类。

在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求的更多相关文章

  1. C# ASP.NET Core使用HttpClient的同步和异步请求

    引用 Newtonsoft.Json // Post请求 public string PostResponse(string url,string postData,out string status ...

  2. 在ASP.NET Core中用HttpClient(二)——发送POST, PUT和DELETE请求

    在上一篇文章中,我们已经学习了如何在ASP.NET Core中使用HttpClient从Web API获取数据.此外,我们还学习了如何使用GetAsync方法和HttpRequestMessage类发 ...

  3. 在ASP.NET Core中用HttpClient(一)——获取数据和内容

    在本文中,我们将学习如何在ASP.NET Core中集成和使用HttpClient.在学习不同HttpClient功能的同时使用Web API的资源.如何从Web API获取数据,以及如何直接使用Ht ...

  4. 在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory

    ​到目前为止,我们一直直接使用HttpClient.在每个服务中,我们都创建了一个HttpClient实例和所有必需的配置.这会导致了重复代码.在这篇文章中,我们将学习如何通过使用HttpClient ...

  5. 在ASP.NET Core中用HttpClient(四)——提高性能和优化内存

    到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容.但是我们可以通过使用流提高性能和优化内存.因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流. 什么是流 流是以文件. ...

  6. 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求

    ​用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...

  7. ASP.NET Core使用HttpClient的同步和异步请求

    using System; using System.Collections.Generic; using System.Collections.Specialized; using System.I ...

  8. ASP.NET Core教程【三】实体字段属性、链接标签、并发数据异常、文件上传及读取

    前文索引:ASP.NET Core教程[二]从保存数据看Razor Page的特有属性与服务端验证ASP.NET Core教程[一]关于Razor Page的知识 实体字段属性 再来看看我们的实体类 ...

  9. asp.net core 系列 9 三种运行环境和IIS发布

    一.在asp.net core中使用多个环境 ASP.NET Core 配置是基于运行时环境, 使用环境变量.ASP.NET Core 在应用启动时读取环境变量ASPNETCORE_ENVIRONME ...

随机推荐

  1. 一些简单的SQL语句

    简单的SQL入门 一,简介 1,  一个数据库包含一个或多个表,表包含带有数据的记录(行) 2,  SQL对大小写不敏感,语句的分号看具体情况 二,语法 1,  数据操作语言:DML a)       ...

  2. 高并发之Semaphore、Exchanger、LockSupport

    本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...

  3. Ubuntu pppoeconf失败

    之前是通过sudo pppoeconf一路yes就可以连通有线网络(dsl和ethernet)的, 系统再次瘫痪后终于进入图形界面, 有线网络丢失, sudo pppoeconf也fail了, 其实加 ...

  4. Apple Watch Series 6 编织表带如何清洗

    Apple Watch Series 6 编织表带如何清洗 如何清洁 Apple Watch https://support.apple.com/zh-cn/HT204522 refs xgqfrms ...

  5. 1 line of CSS Layouts

    1 line of CSS Layouts 10 modern layouts in 1 line of CSS 1. 绝对居中布局 <div class="container&quo ...

  6. UIKit and SwiftUI

    UIKit and SwiftUI Live Preview Try Again or Resume refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许 ...

  7. WebVR & CSS 3D & WebGL

    WebVR & CSS 3D & WebGL VR https://developer.mozilla.org/en-US/docs/Web/API/WebVR_API https:/ ...

  8. Learning JavaScript with MDN (call, apply, bind)

    Learning JavaScript with MDN (call, apply, bind) call, apply, bind Object.prototype.toString() 检测 js ...

  9. 注解处理器APT详解

    本文转载自ANNOTATION PROCESSING 101 Introduction In this blog entry I would like to explain how to write ...

  10. 【SpringMVC】 4.3 拦截器

    SpringMVC学习记录 注意:以下内容是学习 北京动力节点 的SpringMVC视频后所记录的笔记.源码以及个人的理解等,记录下来仅供学习 第4章 SpringMVC 核心技术 4.3 拦截器   ...