在ASP.NET Core中用HttpClient(三)——發送HTTP PATCH請求

在前面的兩篇文章中,咱們討論了不少關於使用HttpClient進行CRUD操做的基礎知識。若是你已經讀過它們,你就知道如何使用HttpClient從API中獲取數據,並使用HttpClient發送POST、PUT和DELETE請求。當咱們使用PUT請求時,用它來更新咱們的資源。但咱們能夠經過使用HTTP PATCH請求進行部分更新來改進這一點。所以,在本文中,咱們將展現如何使用HttpClient發送HTTP PATCH請求來實現資源的部分更新,從而提升應用程序的性能。git

要下載源碼,能夠訪問https://github.com/CodeMazeBlog/httpclient-aspnetcore/tree/patch-with-httpclient獲取項目。github

更多關於HTTP PATCH請求數據庫

正如咱們已經提到的,咱們使用PUT請求進行完整更新,使用PATCH請求進行部分更新。但這並非這兩個HTTP請求之間的惟一區別。首先,請求主體是不一樣的。若是咱們檢查Web API中的PUT,咱們能夠看到請求體是一個簡單的對象:json

[FromBody] CompanyForUpdateDto company

但若是咱們對PATCH請求作一樣的檢查:api

[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操做app

PATCH請求能夠做爲JSON數組的一部分執行一個或多個操做。讓咱們看看PATCH請求的請求體:async

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

正如咱們所看到的,請求主體基本上是一個指定不一樣操做的JSON對象數組。也就是說,咱們能夠確認兩個操做:由op屬性指定的Replace和Remove。路徑部分表示到咱們想要修改的對象屬性的路徑。最後,value部分表示一個新值,咱們用它來替換Name屬性的舊值。ide

使用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類。

原文連接:https://code-maze.com/using-httpclient-to-send-http-patch-requests-in-asp-net-core/

 

相關文章
相關標籤/搜索