我目前有一个实现 RESTFul API 的 Web API。我的 API 模型如下所示:
public class Member
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Created { get; set; }
public DateTime BirthDate { get; set; }
public bool IsDeleted { get; set; }
}
我实现了一个 PUT
方法来更新与此类似的行(为简洁起见,我省略了一些不相关的内容):
[Route("{id}")]
[HttpPut]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
// Do some error checking
// ...
// ...
var myDatabaseEntity = new BusinessLayer.Member(id);
myDatabaseEntity.FirstName = model.FirstName;
myDatabaseEntity.LastName = model.LastName;
myDatabaseEntity.Created = model.Created;
myDatabaseEntity.BirthDate = model.BirthDate;
myDatabaseEntity.IsDeleted = model.IsDeleted;
await myDatabaseEntity.SaveAsync();
}
使用 PostMan ,我可以发送以下 JSON,一切正常:
{
firstName: "Sara",
lastName: "Smith",
created: "2018/05/10",
birthDate: "1977/09/12",
isDeleted: false
}
如果我将它作为我的主体发送到 http://localhost:8311/api/v1/Member/12
作为 PUT 请求,我的数据中 ID 为 12 的记录将更新为您在 JSON 中看到的内容。
不过,我想做的是实现一个 PATCH 动词,我可以在其中进行部分更新。如果 Sara 结婚,我希望能够发送这个 JSON:
{
lastName: "Jones"
}
我希望能够仅发送该 JSON 并仅更新 LastName
字段,而保留所有其他字段。
我试过这个:
[Route("{id}")]
[HttpPatch]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
}
我的问题是,这会返回 model
对象中的所有字段(除了 LastName
字段外,所有字段都是空值),这是有道理的,因为我是说我想要一个 Models.Member
对象。我想知道是否有一种方法可以检测 JSON 请求中实际发送了哪些属性,以便我可以仅更新这些字段?
最佳答案
我希望这有助于使用 Microsoft JsonPatchDocument:
.Net Core 2.1 将补丁操作添加到 Controller 中:
[HttpPatch("{id}")]
public IActionResult Patch(int id, [FromBody]JsonPatchDocument<Node> value)
{
try
{
//nodes collection is an in memory list of nodes for this example
var result = nodes.FirstOrDefault(n => n.Id == id);
if (result == null)
{
return BadRequest();
}
value.ApplyTo(result, ModelState);//result gets the values from the patch request
return NoContent();
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex);
}
}
节点模型类:
[DataContract(Name ="Node")]
public class Node
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "node_id")]
public int Node_id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "full_name")]
public string Full_name { get; set; }
}
仅更新“full_name”和“node_id”属性的有效 Patch JSON 将是一组操作,例如:
[
{ "op": "replace", "path": "full_name", "value": "NewNameWithPatch"},
{ "op": "replace", "path": "node_id", "value": 10}
]
如您所见,“op”是您想要执行的操作,最常见的是“replace”,它只会为新属性设置该属性的现有值,但还有其他操作:
[
{ "op": "test", "path": "property_name", "value": "value" },
{ "op": "remove", "path": "property_name" },
{ "op": "add", "path": "property_name", "value": [ "value1", "value2" ] },
{ "op": "replace", "path": "property_name", "value": 12 },
{ "op": "move", "from": "property_name", "path": "other_property_name" },
{ "op": "copy", "from": "property_name", "path": "other_property_name" }
]
这是我基于C#中的Patch(“替换”)规范使用反射构建的扩展方法,您可以使用它来序列化任何对象以执行Patch(“替换”)操作,您还可以通过所需的Encoding它将返回准备发送到 httpClient.PatchAsync(endPoint, httpContent) 的 HttpContent (StringContent):
public static StringContent ToPatchJsonContent(this object node, Encoding enc = null)
{
List<PatchObject> patchObjectsCollection = new List<PatchObject>();
foreach (var prop in node.GetType().GetProperties())
{
var patch = new PatchObject{ Op = "replace", Path = prop.Name , Value = prop.GetValue(node) };
patchObjectsCollection.Add(patch);
}
MemoryStream payloadStream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(patchObjectsCollection.GetType());
serializer.WriteObject(payloadStream, patchObjectsCollection);
Encoding encoding = enc ?? Encoding.UTF8;
var content = new StringContent(Encoding.UTF8.GetString(payloadStream.ToArray()), encoding, "application/json");
return content;
}
注意到 tt 也使用了我创建的这个类来使用 DataContractJsonSerializer 序列化 PatchObject:
[DataContract(Name = "PatchObject")]
class PatchObject
{
[DataMember(Name = "op")]
public string Op { get; set; }
[DataMember(Name = "path")]
public string Path { get; set; }
[DataMember(Name = "value")]
public object Value { get; set; }
}
如何使用扩展方法和使用 HttpClient 调用补丁请求的 C# 示例:
var nodeToPatch = new { Name = "TestPatch", Private = true };//You can use anonymous type
HttpContent content = nodeToPatch.ToPatchJsonContent();//Invoke the extension method to serialize the object
HttpClient httpClient = new HttpClient();
string endPoint = "https://localhost:44320/api/nodes/1";
var response = httpClient.PatchAsync(endPoint, content).Result;
谢谢
关于c# - Web API 2 - 实现补丁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50215825/