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

swb*_*dit 3 rest updates servicestack

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

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

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

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

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

Sco*_*ott 5

如何处理 RESTful 更新:

如果Bar是一个单独的属性,Foo那么你没有理由不能只拥有一个UpdateFooRequest包含更新的Bar.

像这样:

[Route("/foo/{Id}", "POST")]
public class UpdateFooRequest
{
    public int Id { get; set; }
    public Bar Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果Bar是一个集合使得 Foo 是:

public class Foo
{
    public int Id { get; set; }
    public Bar[] Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后更新Bar项目的更安静的方法Foobar通过Foo路由引用实例。但是您需要知道 bar 的 id,(或集合中相对于它的索引Foo

[Route("/foo/{FooId}/bar/{Id}", "POST")]
public class UpdateFooBarRequest : Bar
{
    public int FooId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在服务器处理部分更新

为了解决这些问题:

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

你唯一需要关心一个整体的时间Foo是什么时候Foo被创建,它会有自己的路线。所以这里应该没什么好担心的。

[Route("/foo", "POST")]
public class CreateFooRequest
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

Foo正在更新你将张贴到这样一个现有的富路线/foo/123

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

最终,您的服务应该能够指示 ORM 仅更新包含在请求中的字段的更改。因此,如果请求只有几个更改,那么 ORM 将处理它。

例如,ORMLite 更新很简单:

db.Update<Foo>(updateFooRequest);
Run Code Online (Sandbox Code Playgroud)

这将UPDATEId仅使用请求中找到的字段标识的行上创建 SQL ;

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

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

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

最后一行where someone assumes it's a full object,有点令人担忧。update 路由应该只用于更新,你不应该在这里使用完整的对象。update 方法应该期望请求Foo是部分的,并且按照我上面的解释进行处理。


我认为您可能应该阅读 ORMLite,它展示了一些很好的示例,说明如何将部分更新应用于现有数据。虽然您不必选择 ORM,但基本概念适用于其他 ORM,我认为这很好地说明了它们。


要按名称更新Bar集合中的项目Foo

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

所以Foo看起来像这样:

public class Foo
{
    public int Id { get; set; }
    public Bar[] Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

让我们假设 Bar

public class Bar
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Baz { get; set; }
    public bool IsEnabled { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果我使用Name而不是IdBar 来更新,我将无法更改该Name属性,因为那时我不知道要更新哪个 Bar。我现在需要一条全新的路线来处理更改 Bar 的名称。

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

[Route("/foo/{FooId}/bar/{Name}", "POST")]
public class UpdateFooBarRequest : Bar
{
    public int FooId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

因此,要更新美孚123的更新Baz = 13,并IsEnabled = false在酒吧名为“鲍勃”

POST /foo/123/bar/Bob
{
    Baz: 13,
    IsEnabled: false
}
Run Code Online (Sandbox Code Playgroud)

在您的操作方法中,您只需要更新与名称“Bob”匹配的 Foo 为 123 的项目。在 ORMLite 中,它看起来像这样:

db.Update(updateBarRequest, p => p.FooId == updateBarRequest.FooId && p.Name == updateBarRequest.Name);
Run Code Online (Sandbox Code Playgroud)

但这样做意味着你不能这样做:

[Route("/foo/{FooId}/bar/{Id}", "POST")]并且[Route("/foo/{FooId}/bar/{Name}", "POST")]因为路由器不知道您是否正在传递一个Id或一个Name所以所以您必须执行非常规的 REST 之类的[Route("/foo/{FooId}/bar/byName-{Name}", "POST")].

我希望这有帮助。