我正在尝试找出一种采用API更改的好方法,我们最近对API进行了更改,以“削弱”我们的对象之一的类型。
过去,用户可以提供对“狗”的引用列表。我们已经决定现在接受“狗”和“猫”。
客户端库使用强类型引用,因此该属性当前看起来像:
public List<IReference<Dog>> occupants { get; set; }
我考虑过将其更改为:
public List<IReference<Animal>> occupants { get; set; }
但是我不喜欢以前的编译时验证现在是运行时(和服务器端)验证。我本来希望输入更强的类型,例如:
public List<Occupant> occupants { get; set; }
public class Occupant : IReference<Animal> {
IReference<Animal> _internalReference;
public Occupant(IReference<Dog> dog) { _internalReference = dog }
public Occupant(IReference<Cat> cat) { _internalReference = cat }
//... (Implement IReference<Animal> as a pass-through to _internalReference)
}
不幸的是,由于C#不允许隐式运算符从接口进行转换,因此更新其客户端库的现有用户将不得不进行数百行代码更改,以将其所有现有Dog
引用“包装”在{ {1}}构造函数。
所以接下来我考虑使用一些辅助方法为列表创建一个新类:
Occupant
不幸的是,C#无法在后台公开覆盖核心public class OccupantList : List<Occupant> {
public void Add(IReference<Dog> newDog) => base.Add(new Occupant(newDog));
public void Add(IReference<Cat> newCat) => base.Add(new Occupant(newCat));
// For backwards compatibility with new list assignments
public static implicit operator OccupantList(List<IReference<Dog>> legacy) =>
new OccupantList(legacy.Select(dog => new Occupant(dog)));
}
实现的方法,各种反射实用程序和我的JSON序列化程序都使用C#来填充此列表,因此当给定要添加的对象时,它会抱怨尚未为ICollection.Add
类型的文件。我个人在使用JSON解串器时遇到问题。我可以为此类编写一个自定义反序列化器,但我以此为标志,试图继承Occupant
并添加对不继承自List<Occupant>
的类型的支持,这注定是失败的。 / p>
有人有什么想法或例子可以指导我们正确的方向吗?
这只是“我的问题”
真正的缺点是,这甚至不是API层的向后不兼容。该API使用动态类型(带有Python解释器的JSON),因此唯一的变化就是以下JSON:
Occupant
成为:
{ "occupants": [{"ref_id":"abc123"}, {"ref_id":"zzz999"}] }
具有向后兼容的规则,如果未指定类型,则将引用视为“狗”。
不幸的是,由于我早先决定向这些引用对象添加编译时类型,因此C#客户端库用户是唯一遭受类型弱化的用户,而API仍能够动态找出合适的类型在运行时。
答案 0 :(得分:2)
您会讨厌这一点,但是如果该API有任何使用者,则最佳实践表明您的重大更改意味着现在应该对API进行版本了。
我没有看到您提到的API控制器的签名,但是如果有问题的模型被定义为该方法的参数,您可能可以摆脱重载控制器方法的麻烦,模型绑定器处理不同的形状,然后在某种中间件或自动映射器中解决该问题。
因为听起来您已经针对某种外部使用发布了此API,您实际上确实不应该违反该输入模型所描述的合同。