gpt4 book ai didi

ServiceStack:RESTful 资源版本控制

转载 作者:行者123 更新时间:2023-12-03 07:39:04 25 4
gpt4 key购买 nike

我已阅读 Advantages of message based web services文章,我想知道是否有推荐的样式/实践来对 ServiceStack 中的 Restful 资源进行版本控制?不同的版本可能会呈现不同的响应或在请求 DTO 中具有不同的输入参数。

我倾向于 URL 类型版本控制(即/v1/movies/{Id}),但我也看到过在 HTTP header 中设置版本的其他做法(即 Content-Type: application/vnd.company. myapp-v2)。

我希望有一种与元数据页面一起使用的方法,但没有那么多的要求,因为我注意到在渲染路由时简单地使用文件夹结构/命名空间就可以很好地工作。

例如(这不会在元数据页面中正确呈现,但如果您知道直接路由/url,则可以正常执行)

  • /v1/movies/{id}
  • /v1.1/movies/{id}

代码

namespace Samples.Movies.Operations.v1_1
{
[Route("/v1.1/Movies", "GET")]
public class Movies
{
...
}
}
namespace Samples.Movies.Operations.v1
{
[Route("/v1/Movies", "GET")]
public class Movies
{
...
}
}

以及相应的服务...

public class MovieService: ServiceBase<Samples.Movies.Operations.v1.Movies>
{
protected override object Run(Samples.Movies.Operations.v1.Movies request)
{
...
}
}

public class MovieService: ServiceBase<Samples.Movies.Operations.v1_1.Movies>
{
protected override object Run(Samples.Movies.Operations.v1_1.Movies request)
{
...
}
}

最佳答案

尝试发展(而不是重新实现)现有服务

对于版本控制,如果您尝试为不同版本端点维护不同的静态类型,您将陷入痛苦的境地。我们最初是沿着这条路线开始的,但是一旦您开始支持您的第一个版本,维护同一服务的多个版本的开发工作就会爆炸,因为您将需要维护不同类型的手动映射,这很容易导致必须维护多个版本并行实现,每个都耦合到不同的版本类型 - 严重违反了 DRY。对于动态语言来说,这不是一个问题,因为相同的模型可以很容易地被不同版本重复使用。

利用序列化器中的内置版本控制

我的建议是不要明确版本化,而是利用序列化格式内的版本控制功能。

例如:您通常不需要担心 JSON 客户端的版本控制,因为 JSON and JSV Serializers are much more resilient 的版本控制功能.

防御性地增强现有服务

使用 XML 和 DataContract,您可以自由添加和删除字段,而无需进行重大更改。如果您将 IExtensibleDataObject 添加到您的响应 DTO,您还有可能访问 DTO 上未定义的数据。我的版本控制方法是防御性编程,这样就不会引入重大更改,您可以使用旧的 DTO 进行集成测试来验证这种情况。以下是我遵循的一些提示:

  • 切勿更改现有属性的类型 - 如果您需要将其设为不同类型,请添加另一个属性并使用旧的/现有的属性来确定版本
  • 程序会防御性地意识到旧客户端不存在哪些属性,因此不要强制执行它们。
  • 保留单个全局命名空间(仅与 XML/SOAP 端点相关)

我通过使用每个 DTO 项目的 AssemblyInfo.cs 中的 [assemble] 属性来完成此操作:

[assembly: ContractNamespace("http://schemas.servicestack.net/types", 
ClrNamespace = "MyServiceModel.DtoTypes")]

程序集属性使您无需在每个 DTO 上手动指定显式命名空间,即:

namespace MyServiceModel.DtoTypes {
[DataContract(Namespace="http://schemas.servicestack.net/types")]
public class Foo { .. }
}

如果您想使用与上述默认 namespace 不同的 XML namespace ,您需要注册它:

SetConfig(new EndpointHostConfig {
WsdlServiceNamespace = "http://schemas.my.org/types"
});

在 DTO 中嵌入版本控制

大多数时候,如果您进行防御性编程并优雅地发展您的服务,您将不需要确切地知道特定客户端正在使用哪个版本,因为您可以从填充的数据中推断出它。但在极少数情况下,您的服务需要根据客户端的特定版本调整行为,您可以将版本信息嵌入到 DTO 中。

通过您发布的 DTO 的第一个版本,您可以愉快地创建它们,而无需考虑版本控制。

class Foo {
string Name;
}

但也许由于某种原因,表单/UI 发生了更改,您不再希望客户端使用不明确的 Name 变量,并且您还希望跟踪客户端正在使用的特定版本:

class Foo {
Foo() {
Version = 1;
}
int Version;
string Name;
string DisplayName;
int Age;
}

后来在团队 session 上讨论过,DisplayName 不够好,您应该将它们分成不同的字段:

class Foo {
Foo() {
Version = 2;
}
int Version;
string Name;
string DisplayName;
string FirstName;
string LastName;
DateTime? DateOfBirth;
}

因此,当前状态是您有 3 个不同的客户端版本,现有调用如下所示:

v1 版本:

client.Post(new Foo { Name = "Foo Bar" });

v2 版本:

client.Post(new Foo { Name="Bar", DisplayName="Foo Bar", Age=18 });

v3 版本:

client.Post(new Foo { FirstName = "Foo", LastName = "Bar", 
DateOfBirth = new DateTime(1994, 01, 01) });

您可以继续在同一实现中处理这些不同版本(将使用最新的 v3 版本的 DTO),例如:

class FooService : Service {

public object Post(Foo request) {
//v1:
request.Version == 0
request.Name == "Foo"
request.DisplayName == null
request.Age = 0
request.DateOfBirth = null

//v2:
request.Version == 2
request.Name == null
request.DisplayName == "Foo Bar"
request.Age = 18
request.DateOfBirth = null

//v3:
request.Version == 3
request.Name == null
request.DisplayName == null
request.FirstName == "Foo"
request.LastName == "Bar"
request.Age = 0
request.DateOfBirth = new DateTime(1994, 01, 01)
}
}

关于ServiceStack:RESTful 资源版本控制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12400071/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com