ServiceStack:RESTful资源版本控制

Ste*_*son 37 servicestack

我已经阅读了 基于消息的Web服务优势的文章,我想知道是否有推荐的样式/实践来对ServiceStack中的Restful资源进行版本控制?不同的版本可以在请求DTO中呈现不同的响应或具有不同的输入参数.

我倾向于URL类型版本控制(即/ v1/movies/{Id}),但我已经看到了在HTTP头中设置版本的其他实践(即Content-Type:application/vnd.company.myapp-v2 ).

我希望有一种方法可以使用元数据页面但不是一个需求,因为我注意到只需使用文件夹结构/命名空间在渲染路径时工作正常.

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

  • / V1 /电影/ {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
    {
       ...
    }   
}
Run Code Online (Sandbox Code Playgroud)

和相应的服务......

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)
        {
        ...
        }
    }
Run Code Online (Sandbox Code Playgroud)

myt*_*thz 60

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

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

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

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

例如:您通常不需要担心使用JSON客户端进行版本控制,因为JSON和JSV Serializer的版本控制功能更具弹性.

防御性地增强您现有的服务

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

  • 永远不要更改现有属性的类型 - 如果需要它是另一种类型,请添加另一个属性并使用旧/现有属性来确定版本
  • 程序防御性地意识到老客户不存在哪些属性,所以不要强制它们.
  • 保留单个全局命名空间(仅与XML/SOAP端点相关)

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

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

assembly属性使您无需在每个DTO上手动指定显式名称空间,即:

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

如果要使用与上述默认名称不同的XML命名空间,则需要将其注册为:

SetConfig(new EndpointHostConfig {
    WsdlServiceNamespace = "http://schemas.my.org/types"
});
Run Code Online (Sandbox Code Playgroud)

在DTO中嵌入版本控制

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

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

class Foo {
  string Name;
}
Run Code Online (Sandbox Code Playgroud)

但也许由于某种原因,Form/UI已更改,您不再希望客户端使用模糊的Name变量,并且您还想跟踪客户端使用的特定版本:

class Foo {
  Foo() {
     Version = 1;
  }
  int Version;
  string Name;
  string DisplayName;
  int Age;
}
Run Code Online (Sandbox Code Playgroud)

后来在团队会议中讨论过,DisplayName不够好,你应该将它们分成不同的字段:

class Foo {
  Foo() {
     Version = 2;
  }
  int Version;
  string Name;
  string DisplayName;
  string FirstName;
  string LastName;  
  DateTime? DateOfBirth;
}
Run Code Online (Sandbox Code Playgroud)

所以当前状态是你有3个不同的客户端版本,现有的调用看起来像:

v1发布:

client.Post(new Foo { Name = "Foo Bar" });
Run Code Online (Sandbox Code Playgroud)

v2发布:

client.Post(new Foo { Name="Bar", DisplayName="Foo Bar", Age=18 });
Run Code Online (Sandbox Code Playgroud)

v3发布:

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

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢mythz!我之前使用过url版本,它基本上演变为维护不同版本的多个代码库.我将尝试使用这种方法进行演变. (2认同)