如何使用restful API实现对象属性更新?

时间:2013-12-30 16:50:07

标签: rest servicestack updates

使用ServiceStack(面向消息的RESTful Web服务)实现对象属性更新的正确方法是什么?

如果客户端需要更新Foo.bar并且只有Foo的id。

我是否应该接受包含Foo id和Foo.bar值的FooBarUpdate请求对象? 这意味着我必须为每个属性更新创建单独的请求,或者至少为我期望更新的那些(或它们的组合)创建。这似乎也不符合RESTful标准。

或者,如果我接受包含具有更新属性的整个Foo的Foo请求对象,则客户端首先需要检索Foo,然后服务将检查哪些属性已更新。这似乎是一种不必要的开销。

最后,如果我接受一个Foo请求,但只有id和修改后的属性值(Foo.bar),那么Foo对象是不完整的,根据其他帖子,如果有人认为它是一个完整的对象,这可能会导致混淆

1 个答案:

答案 0 :(得分:4)

如何处理RESTful更新:

如果BarFoo上的个人财产,那么您没有理由不能拥有包含更新UpdateFooRequest的{​​{1}}。

像这样:

Bar

但是如果[Route("/foo/{Id}", "POST")] public class UpdateFooRequest { public int Id { get; set; } public Bar Bar { get; set; } } 是一个集合,那么Foo就是:

Bar

然后,更新public class Foo { public int Id { get; set; } public Bar[] Bar { get; set; } } Bar项的更加安静的方法是通过Foo路由引用bar的实例。但是你需要知道条形码的id,(或相对于它的集合中的索引Foo

Foo

处理服务器上的部分更新

解决这些问题:

  

或者,如果我接受包含具有更新属性的整个Foo的[Route("/foo/{FooId}/bar/{Id}", "POST")] public class UpdateFooBarRequest : Bar { public int FooId { get; set; } } 请求对象,则客户端首先需要检索Foo,然后该服务将检查哪些属性已更新。这似乎是一种不必要的开销。

您需要关注整个Foo的唯一时间是创建Foo时,它将拥有自己的路线。所以这里应该没什么可担心的。

Foo

[Route("/foo", "POST")] public class CreateFooRequest { ... } 更新时,您将发布到现有的Foo路线,例如Foo

/foo/123

最终,您的服务应该能够指示ORM仅使用更改更新请求中包含的字段。因此,如果请求只有一些更改,那么ORM将处理该问题。

例如,ORMLite更新只是:

[Route("/foo/{Id}", "POST")]
public class UpdateFooRequest
{
    public int Id { get; set; }
    ...
}

这将在db.Update<Foo>(updateFooRequest); 标识的行上仅使用请求中找到的字段创建SQL UPDATE;

如果发送了整个Id,那么允许ORM将其视为更改许多字段,该记录将被覆盖。您可以 在更新之前按照您的建议查找记录,并确定更改的字段,但正如您所说,这是一个不必要的开销。

通常,如果您的更新方法处理部分更改,那么发送整个Foo不应该有问题,但理想情况下,客户端应该只发送更改。

  

最后,如果我接受一个Foo请求但只有id和修改后的属性值(Foo.bar),那么Foo对象就不完整了,根据其他帖子,这可能会导致混淆,因为有人认为它是一个完整的对象。

最后一行Foo略显令人担忧。更新路径应仅用于更新,您不应该在此处使用完整对象。更新方法应该期望请求where someone assumes it's a full object是部分的,并按照我上面的解释处理。


我认为你应该read up on ORMLite它显示了一些很好的例子,说明如何将部分更新应用于现有数据。虽然它不是您选择的ORM所必需的,但基本概念适用于其他ORM,我认为这很好地说明了它们。


通过名称更新Foo集合中的项目Bar

这样做非常不寻常。使用具有含义的值(如Name)的一个缺点是,您无法轻松更改此属性 - 因为它是标识符。如果它是静态的那么它就没问题了。

所以Foo看起来像这样:

Foo

我们假设public class Foo { public int Id { get; set; } public Bar[] Bar { get; set; } }

Bar

如果我使用Bar的public class Bar { public int Id { get; set; } public string Name { get; set; } public int Baz { get; set; } public bool IsEnabled { get; set; } } 而非Name来更新,我无法更改Id属性,因为我不知道哪个Bar更新。我现在需要一条全新的路线来处理改变Bar的名字。

如果您决定将其用作集合中的标识符,则可以执行以下操作:

Name

所以要更新Foo 123,并在名为&#34的Bar上更新[Route("/foo/{FooId}/bar/{Name}", "POST")] public class UpdateFooBarRequest : Bar { public int FooId { get; set; } } Baz = 13; Bob&#34;

IsEnabled = false

在您的操作方法中,您只需要更新与名称匹配的项目&#34; Bob&#34;拥有Foo 123.在ORMLite中,它看起来像这样:

POST /foo/123/bar/Bob
{
    Baz: 13,
    IsEnabled: false
}

但这样做意味着你无法做到:

db.Update(updateBarRequest, p => p.FooId == updateBarRequest.FooId && p.Name == updateBarRequest.Name); [Route("/foo/{FooId}/bar/{Id}", "POST")],因为路由器不知道您是否正在传递[Route("/foo/{FooId}/bar/{Name}", "POST")]Id,因此您必须执行Name之类的非常规REST {1}}。

我希望这会有所帮助。