CRUD API:更新するフィールドをどのように指定しますか?


9

ある種のデータベースに永続化されているある種のデータ構造があるとします。簡単にするために、このデータ構造を呼びましょうPerson。これで、他のアプリケーションがを作成、読み取り、更新、および削除できるようにするCRUD APIを設計する必要がありますPerson。簡単にするために、このAPIが何らかのWebサービスを介してアクセスされると仮定します。

CRUDのC、R、Dパーツのデザインはシンプルです。私はC#のような関数表記を使用します-実装はSOAP、REST / JSON、またはその他の可能性があります。

class Person {
    string Name;
    DateTime? DateOfBirth;
    ...
}

Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);

アップデートはどうですか?自然なことは

void UpdatePerson(Identifier, Person);

しかし、どのフィールドを更新するかどのように指定ますPersonか?


私が思いつくことができる解決策:

  • 常に完全な Personを渡すように要求することができます。つまり、クライアントは次のようにして生年月日を更新します。

    p = GetPerson(id);
    p.DateOfBirth = ...;
    UpdatePerson(id, p);
    

    ただし、そのためには、GetとUpdateの間にトランザクションの整合性またはロックが必要になります。そうしないと、他のクライアントによって並行して行われた他の変更を上書きする可能性があります。これにより、APIがさらに複雑になります。さらに、次の疑似コード(JSONをサポートするクライアント言語を想定)であるため、エラーが発生しやすくなります。

    UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
    

    -これ正しいように見えます-DateOfBirthを変更するだけでなく、他のすべてのフィールドをnullにリセットします。

  • であるすべてのフィールドを無視できますnull。しかし、それを変更しないこと DateOfBirthと、意図的にnullに変更することの違いをどのように作成しますか?

  • 署名をに変更しますvoid UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)

  • 署名をに変更しますvoid UpdatePerson(Identifier, ListOfFieldValuePairs)

  • 送信プロトコルのいくつかの機能を使用します。たとえば、PersonのJSON表現に含まれていないすべてのフィールドを無視できます。ただし、通常は、JSONを自分で解析し、ライブラリの組み込み機能(WCFなど)を使用できないようにする必要があります。

ソリューションはどれも私には本当にエレガントに思えません。確かに、これは一般的な問題なので、誰もが使用するベストプラクティスソリューションは何ですか?


識別子が人の一部ではないのはなぜですか?Personまだ永続化されていない新しく作成されたインスタンスの場合、識別子が永続化メカニズムの一部として決定された場合は、nullのままにします。答えは、JPAはバージョン番号を使用します。バージョン23を読み取る場合、アイテムを更新すると、DBのバージョンが24の場合、書き込みは失敗します。
SJuan76 2015

PUTPATCHメソッドの両方を許可して通信します。を使用する場合はPATCH、送信キーのみを置き換え、PUTオブジェクト全体を置き換える必要があります。
Lode

回答:


8

このオブジェクトの要件としての変更の追跡がない場合(たとえば、「ユーザーJohnが名前と生年月日を変更した」)、最も簡単なのは、DB内のオブジェクト全体をコンシューマーから受け取ったオブジェクトでオーバーライドすることです。このアプローチでは、ワイヤーを介して送信されるデータが若干増えますが、更新前の読み取りは回避されます。

活動追跡の要件がある場合。あなたの世界ははるかに複雑であり、CRUDアクションに関する情報を格納する方法とそれらをインターセプトする方法を設計する必要があります。それはあなたがそのような必要条件を持っていないなら、あなたが飛び込みたくない世界です。

個別のトランザクションで値をオーバーライドすることにより、楽観的ロック悲観的ロックに関する研究を行うことをお勧めします。彼らはこの一般的なシナリオを軽減します:

  1. オブジェクトはuser1によって読み取られます
  2. オブジェクトはuser2によって読み取られます
  3. user1によって書き込まれたオブジェクト
  4. user2によって書き込まれたオブジェクトとuser1によって上書きされた変更

ユーザーごとにトランザクションが異なるため、これを使用する標準SQLがあります。最も一般的なのは楽観的ロックです(バージョンに関するコメントで@ SJuan76でも言及されています)。DB内のレコードのバージョン。書き込み時に、バージョンが一致する場合は、最初にDBを調べます。バージョンが一致しない場合、その間に誰かがオブジェクトを更新したことを知っており、この状況についてコンシューマにエラーメッセージで応答する必要があります。はい、この状況をユーザーに示す必要があります。

書き込み前にDBから実際のレコードを読み取る必要があることに注意してください(楽観的ロックバージョンの比較のため)。デルタロジック(変更された値のみを書き込む)を実装すると、書き込み前に追加の読み取りクエリが不要になる場合があります。

デルタロジックの配置は、コンシューマとの契約に大きく依存しますが、コンシューマにとって最も簡単なのは、デルタの代わりに完全なペイロードを構築することです。


2

作業中のPHP APIがあります。更新の場合、JSONオブジェクトでフィールドが送信されない場合、フィールドはNULLに設定されます。次に、すべてをストアドプロシージャに渡します。ストアード・プロシージャーは、field = IFNULL(input、field)ですべてのフィールドを更新しようとします。したがって、JSONオブジェクトに1つのフィールドしかない場合、そのフィールドのみが更新されます。設定されたフィールドを明示的に空にするには、field = ''にする必要があります。DBは、空の文字列またはその列のデフォルト値でフィールドを更新します。


3
まだnullでないフィールドを意図的にnullに設定するにはどうすればよいですか?
Robert Harvey

すべてのフィールドはとてもCHARフィールドは「」を取得デフォルトおよびすべての整数フィールドは0を取得し、NOT NULLを設定している
ジャレドBernacchi

1

クエリ文字列で更新されたフィールドリストを指定します。

PUT /resource/:id?fields=name,address,dob Body { //resource body }

保存されたデータをリクエストボディからのモデルとマージする実装:

private ResourceModel MergeResourceModel(ResourceModel original, ResourceModel updated, List<string> fields)
{
    var comparer = new FieldComparer();

    foreach (
            var item in
            typeof (ResourceModel).GetProperties()
                    .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof (JsonIgnoreAttribute))))
    {
        if (fields.Contains(item.Name, comparer))
        {
            var property = typeof (ResourceModel).GetProperty(item.Name);
            property.SetValue(original, property.GetValue(updated));
        }
    }

    return original;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.