it-swarm.com.ru

Web API 2 - реализация PATCH

В настоящее время у меня есть веб-API, который реализует RESTFul 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, запись в моих данных с идентификатором 12 будет обновлена ​​до того, что вы видите в JSON.

Что я хотел бы сделать, так это реализовать глагол PATCH, где я могу делать частичные обновления. Если Сара выходит замуж, я хотел бы иметь возможность отправить этот 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, чтобы я мог обновить только эти поля?

6
Icemanind

Операции PATCH обычно не определяются с использованием той же модели, что и операции POST или PUT, именно по этой причине: как вы различаете null и don't change. Из IETF :

Однако с помощью PATCH вложенный объект содержит набор инструкции, описывающие, как ресурс в данный момент находится на Исходный сервер должен быть модифицирован для создания новой версии.

Вы можете посмотреть здесь для их предложения 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" }
]
9
Tipx

Ответ @ Tipx на использование PATCH очевиден, но, как вы, вероятно, уже нашли, на самом деле добиться этого в статически типизированном языке, таком как C #, нетривиальное упражнение.

В случае, когда вы используете PATCH для представления набора частичных обновлений для одной сущности домена (например, для обновления имени и фамилии только для контакта со многими другими свойствами), вам нужно сделать что-то вроде зацикливание каждой инструкции в запросе PATCH и затем применение этой инструкции к экземпляру вашего класса.

Применение индивидуальной инструкции будет состоять из 

  • Поиск свойства экземпляра, соответствующего имени в инструкции , Или обработка имен свойств, которые вы не ожидали
  • Для обновления: Попытка проанализировать значение, представленное в патче, в свойстве экземпляра и обработать ошибку, если, например, свойство экземпляра является логическим, но инструкция исправления содержит дату
  • Решите, что делать с инструкциями Add, поскольку вы не можете добавлять новые свойства в статически типизированный класс C #. Один из подходов состоит в том, чтобы сказать, что Add означает «установить значение свойства экземпляра, только если существующее значение свойства равно нулю»

Для веб-API 2 в полной версии .NET Framework проект GSON JSONPatch , похоже, пытается обеспечить этот код, хотя не похоже, что в последнее время для этого репозитория была проведена большая разработка, и readme действительно заявляет : 

Это все еще очень ранний проект, не используйте его в производстве пока, если вы не понимаете источник и не возражаете исправить несколько ошибок ;)

В .NET Core все проще, так как он имеет набор функций для поддержки этого в пространстве имен Microsoft.AspNetCore.JsonPatch .

Довольно полезный jsonpatch.com site также перечисляет еще несколько опций для Patch в .NET:

  • Asp.Net Core JsonPatch (официальная реализация Microsoft)
  • Ramone (среда для использования REST сервисов, включает реализацию JSON Patch)
  • JsonPatch (Добавляет поддержку JSON Patch в ASP.NET Web API)
  • Starcounter (In-memory Application Engine, использует JSON Patch с OT для синхронизации клиент-сервер)
  • Nancy.JsonPatch (Добавляет поддержку JSON Patch в NancyFX)
  • Manatee.Json (JSON-все, включая JSON Patch)

Мне нужно добавить эту функциональность в наш существующий проект Web API 2, поэтому я обновлю этот ответ, если найду что-нибудь еще полезное при этом.

0
tomRedox