14

Скажите у вас есть метод хранилища для обновления документа:Частичное Entity Обновления в WebAPI PUT/POST

public Document UpdateDocument(Document document) 
    { 
    Document serverDocument = _db.Documents.Find(document.Id); 
    serverDocument.Title = document.Title; 
    serverDocument.Content = document.Content; 
    _db.SaveChanges(); 
    return serverDocument; 
    } 

В этом случае предприятие имеет два свойства. При обновлении документа, оба этих свойства необходимы в запросе JSON, поэтому запрос на PUT /api/folder с телом

{ 
    "documentId" = "1", 
    "title" = "Updated Title" 
} 

будет возвращать ошибку, потому что «содержание» не было. Причина, по которой я это делаю, состоит в том, что даже для свойств и свойств, которые могут быть недействительными, которые пользователь не обновляет, кажется более безопасным заставить клиента указать эти поля в запросе, чтобы избежать перезаписи неопределенных полей с нулевыми серверами.

Это привело меня к практике всегда требовать каждого обновляемого свойства в запросах PUT и POST, даже если это означает указание значения null для этих свойств.

Это круто, или есть образец/практика, о которых я еще не узнал, которые могли бы облегчить частичные обновления, отправив только то, что нужно по проводу?

ответ

17

Лучшей практикой в ​​разработке API является использование HTTP PATCH для частичных обновлений. На самом деле, такие случаи, как ваша, являются самой причиной, по которой IETF представил ее в первую очередь.

RFC 5789 определяет его очень точно:

ЗАПЛАТА используется для применения частичных модификаций к ресурсу.

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

Марк Ноттингем написал большую статью об использовании PATCH дизайна API - http://www.mnot.net/blog/2012/09/05/patch

В вашем случае, это было бы:

[AcceptVerbs("PATCH")] 
    public Document PatchDocument(Document document) 
    { 
     Document serverDocument = _db.Documents.Find(document.Id); 
     serverDocument.Title = document.Title; 
     serverDocument.Content = document.Content; 
     _db.SaveChanges(); 
     return serverDocument; 
    } 
+1

Пример не будет работать с Content-Type: application/json-patch {"replace": "/ count", "value": 5}, если только не был фильтр действий или формат медиафайла, чтобы преобразовать схему патча в объект – Clive

+1

Этот ответ абсолютно непрактичен. Что делать, если вы хотите обновить несколько других полей в «Документе»? Как вы узнаете, какие из этих полей необходимо обновить? Нулевое название означает удаление или не обновляется? Без правильного формата патча невозможно понять такие вещи. AFAIK, поддержка JSON-Patch ASP.Net в мире отсутствует. Мне бы хотелось, чтобы я был ошибочен. О, и я большой поклонник твоих статей!:) – Mrchief

+0

Вспомните, что «Дельта» OData может помочь, хотя я не уверен, что вы можете использовать ее, не вступая в брак в OData в целом. – Mrchief

3

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

Хорошая практика делает POST или PUT должен включать только те значения, которые вам нужны для этого конкретного запроса. Выполняя UpdateDocument, вы должны спросить себя, что «действительно должно быть сделано здесь»? Если у вас есть сто полей на этом объекте, вам необходимо обновить их или только часть из них. Какое «действие» вы действительно пытаетесь сделать?

Давайте иллюстрацию для этих вопросов, скажем, у нас есть User объект, который имеет следующие поля:

public class User { 
    public int Id {get;set;} 
    public string Username {get;set;} 
    public string RealName {get;set;} 
    public string Password {get;set;} 
    public string Bio {get;set;} 
} 

Вы тогда два случая использования:

  1. Обновление профиля Пользователя
  2. Обновление пароля пользователя

Когда вы делаете каждый из них, вы не будете, или это хорошая идея, иметь один метод обновления, который будет делать оба. Вместо того, чтобы иметь общий UpdateUser метод вы должны иметь следующие методы:

  1. UpdateProfile
  2. UpdatePassword

методы, которые принимает поля, что они просто нужно, ни больше, ни меньше.

public User UpdateProfile(int id, string username, string realname, string bio) { 
} 
public User UpdatePassword(int id, string password) { 
} 

Теперь возникает вопрос:

У меня есть случай использования, что «действие пользователя» позволяет получить обновленную информацию о нескольких областях, где некоторые из полей не может быть «не вход» от , но я не хочу обновлять это поле в своей модели.

Предположим, что пользователь обновляет его/ее профиль и при условии значения Username, RealName, но не для Bio. Но вы не хотите устанавливать Bio как null или empty, если оно уже имеет значение. Затем это становится частью бизнес-логики вашего приложения и , которое должно обрабатываться явно.

public User UpdateProfile(int id, string username, string realname, string bio) { 
    var user = db.Users.Find(id); 
    // perhaps a validation here (e.g. if user is not null) 
    user.Username = username; 
    user.RealName = realname; 
    if (!string.IsNullOrEmptyWHiteSpace(bio)) { 
     user.Bio = bio; 
    } 
} 
+1

эти подписи метод не будет работать с ASP.NET Web API как: 1) он не будет рассматривать тело для примитивных параметров, если явно не указано с использованием атрибута * [FromBody] *, а 2) он может связывать только один объект из тела в любом случае –

+0

В соответствии с спецификацией HTTP вы не должен использовать PUT для doin g частичных обновлений. Вы можете делать все, что хотите, с помощью POST. –