Какой рекомендуемый в настоящее время способ частичного обновления веб-API?

Мне интересно, как реализовать частичные обновления с помощью интерфейса ASP.NET Web API RESTful? Скажем, например, мы передаем объекты по проводу следующей структуры:

public class Person {
    public int Id { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
}

Как можно поддерживать обновление только частей Person за раз, например, свойство Email? Рекомендуется ли это реализовать с помощью OData и глагола PATCH, или было бы лучше реализовать PATCH самостоятельно?

Ответ 1

Нет поддержки в текущей последней стабильной версии Web API (с августа 2012 года). Поэтому, если все, что вы хотите использовать, это RTM для веб-API, вам придется реализовать всю сантехнику самостоятельно.

С учетом этого, пакет OData preerelease поддерживает частичные обновления очень хорошо с помощью нового объекта Delta<T>. В настоящее время пакет Microsoft.AspNet.WebApi.OData уже находится в версии RC (0.3) и может быть получен здесь: http://www.nuget.org/packages/Microsoft.AspNet.WebApi.OData

Как только вы установите это, вы можете использовать это соответственно:

[AcceptVerbs("PATCH")]
public void Patch(int id, Delta<Person> person)
{
    var personFromDb = _personRepository.Get(id);
    person.Patch(personFromDb);
    _personRepository.Save();
}

И вы бы назвали его от клиента следующим образом:

$.ajax({
    url: 'api/person/1',
    type: 'PATCH',
    data: JSON.stringify(obj),
    dataType: 'json',
    contentType: 'application/json',
    success: function(callback) {            
       //handle errors, do stuff yada yada yada
    }
});

Очевидным преимуществом этого является то, что он работает для любого свойства, и вам не нужно заботиться о том, обновляете ли вы Email или Username или еще что-то.

Вы также можете посмотреть этот пост, поскольку он показывает очень похожую технику http://techbrij.com/http-patch-request-asp-net-webapi

EDIT (подробнее): Чтобы просто использовать PATCH, вам не нужно включать что-либо связанное с OData, за исключением добавления пакета OData - для доступа к объекту Delta<TEntityType>.

Затем вы можете сделать следующее:

public class ValuesController : ApiController
{
    private static List<Item> items = new List<Item> {new Item {Id = 1, Age = 1, Name = "Abc"}, new Item {Id = 2, Age = 10, Name = "Def"}, new Item {Id = 3, Age = 100, Name = "Ghj"}};

    public Item Get(int id)
    {
        return items.Find(i => i.Id == id);
    }

    [AcceptVerbs("PATCH")]
    public void Patch(int id, Delta<Item> item)
    {
        var itemDb = items.Find(i => i.Id == id);
        item.Patch(itemDb);
    }
}

Если ваш предмет, скажем:

{
    "Id": 3,
    "Name": "hello",
    "Age": 100
}

Вы можете PATCH на /api/values/3 с помощью:

{
    "Name": "changed!"
}

и это будет правильно обновлять ваш объект.

Delta<TEntity> будет отслеживать изменения для вас. Это динамический класс, который действует как легкий прокси для вашего Типа и будет понимать различия между исходным объектом (т.е. Из БД) и тем, который был передан клиентом.

Это НЕ будет влиять на остальную часть вашего API каким-либо образом (за исключением, конечно, замены библиотек DLL более новыми для облегчения зависимостей пакета OData).

Я добавил пример проекта, чтобы продемонстрировать работу PATCH + Delta - вы можете его захватить здесь (it.s VS2012) https://www.dropbox.com/s/hq7wt3a2w84egbh/MvcApplication3.zip