現在、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
}
これをPUTリクエストとしてhttp://localhost:8311/api/v1/Member/12
にボディとして送信すると、IDが12のデータ内のレコードがJSONに表示されるものに更新されます。
しかし、私がやりたいことは、部分的な更新を行うことができるPATCH verbを実装することです。サラが結婚したら、この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リクエストで実際に送信されたプロパティを検出する方法があり、それらのフィールドだけを更新できるかどうかです?
PATCH
操作は、通常、正確にその理由でPOST
またはPUT
操作と同じモデルを使用して定義されません:null
とdon't change
。 IETFから :
ただし、PATCHの場合、囲まれたエンティティには、新しいバージョンを生成するために、Originサーバーに現在存在するリソースを変更する方法を説明する一連の指示が含まれています。
here のPATCH
提案を見ることができますが、sumarillyは次のとおりです。
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
これがMicrosoft JsonPatchDocumentの使用に役立つことを願っています。
コントローラーへの.Net Core 2.1パッチアクション:
[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」プロパティのみを更新する有効なパッチ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( "replace")仕様に基づいて作成した拡張メソッドで、これを使用してオブジェクトをシリアル化してPatch( "replace")操作を実行できます。 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;
ありがとう
PATCH
を使用した@Tipxの答えはスポットオンですが、おそらく既にお気付きのように、C#のような静的に型付けされた言語で実際にそれを達成するのは簡単なことではありません。
PATCH
を使用して単一のドメインエンティティの部分更新セットを表す場合(たとえば、より多くのプロパティを持つ連絡先の姓と名のみを更新する場合) 「PATCH」リクエスト内の各命令をループし、その命令をクラスのインスタンスに適用するラインに沿った何か。
個々の指示の適用は、
完全な.NET Framework上のWeb API 2の場合、 JSONPatch githubプロジェクト は、このコードを提供する際に突き刺すように見えますが、最近そのリポジトリで多くの開発が行われているようには見えません。 readmeの状態:
これはまだ初期のプロジェクトです。ソースを理解し、いくつかのバグを修正することを気にしない限り、まだ本番環境で使用しないでください。
.NET Coreでは、 Microsoft.AspNetCore.JsonPatch
名前空間 。
かなり便利な jsonpatch.com サイトには、.NETのPatchのいくつかのオプションもリストされています。
- Asp.Net Core JsonPatch (Microsoft公式実装)
- Ramone (RESTサービスを使用するためのフレームワーク、JSONパッチ実装を含む)
- JsonPatch (ASP.NET Web APIにJSONパッチサポートを追加)
- Starcounter (インメモリアプリケーションエンジン、OTクライアントとサーバーの同期にJSONパッチを使用)
- Nancy.JsonPatch (NancyFXにJSONパッチサポートを追加)
- Manatee.Json (JSONパッチを含むすべてのJSON)
私たちの既存のWeb API 2プロジェクトにこの機能を追加する必要があるので、それを行うときに役立つ何か他のものが見つかった場合は、この回答を更新します。