未来证明:删除JSON信封是当前的最佳做法吗?

时间:2017-02-13 15:15:41

标签: json rest solid-principles

我读过有冲突的"意见"是否应该在JSON请求中删除信封或回复。

示例:

{
    "data": {
        "foo" : "bar",
        "baz" : "Xyzzy"
    }
}

应该(假设)写成:

{
    "foo" : "bar",
    "baz" : "Xyzzy"
}

但是,为了与SOLID原则保持一致,此结构应该是可以扩展的,但是关闭以进行修改。因此,删除信封将是一个坏主意。正确?

如果稍后我决定需要向入站JSON信息添加更多信息,那么执行此操作会更加清晰:

{
    "data": {
        "foo" : "bar",
        "baz" : "Xyzzy"
    },

    "extended-data": {
        "abc" : 123
    }
}

比这样做:

{
    "foo" : "bar",
    "baz" : "Xyzzy",
    "abc" : 1234
}

前者允许先前编写的代码,用于查找"数据"节点执行没有失败或更改。后者要求重写代码以寻找新值。

目前的最佳做法是什么,请找到您的来源:我需要接受的标准而不是意见。

更新

要回答异议:"如果添加字段,则必须更改代码。"

不是真的。我不必更改代码来处理新字段,我只需要为新数据添加新的处理程序:

示例:

function delegateTask($json) {
    $this->doSomething($json->data);
}
扩展后

function delegateTask($json) {
    $this->doSomething($json->data);
    $this->doSomethingElse($json->extended);
}

如果我只使用HTTP作为信封,我必须重写doSomething()。如果我以SOLID方式执行,我只需要添加一个查看新数据的函数。

不重复: 这个问题不是When in my REST API should I use an envelope? If I use it in one place, should I always use it?的重复,因为这个问题具体涉及S.O.L.I.D原则以及与信封相关的代码扩展。不只是将错误消息返回给客户端。

2 个答案:

答案 0 :(得分:1)

信封是名称空间。命名空间是模块化设计和关注点分离的好工具。

考虑用于使用JSON API的事物类型。网格等将实际数据与分页和其他元数据分开,不那么脆弱。

我认为你可以在SOLID中应用“我”。

接口隔离原则 - “许多客户端特定接口优于一个通用接口。

使用信封,我可以采用任何API架构并将其与我选择的任何Javascript组件相结合,而无需担心冲突或必须重命名字段以使事情发挥作用。

这同样适用于API聚合。我可以在一次调用中组合2个单独的结果,以提供对特定用途最有效的“视图模型”。更容易与信封聚合。

关注像OData这样的标准,这个标准背后有许多聪明人。

就扩展和生命周期而言,我只是发布一个v2 API并维护旧的API,直到它被弃用。 API的多版本是常见做法(例如:eBay,Salesforce)。

答案 1 :(得分:1)

据我所知,这实际上是关于消息处理,而不是特定于JSON,REST等。

  

前者允许以前编写的代码,它寻找“数据”节点执行而不会出现故障或更改。后者要求重写代码以寻找新值。

     

目前最佳做法是什么,请找到您的来源:我需要接受的标准而不是意见。

我所知道的最好的参考是Versioning in an Event Sourced System,Greg Young。

简而言之

  • 消费者总是为他们希望从消息中读取的任何数据准备一个默认值,以涵盖未提供数据的情况
  • 消费者忽略他们不理解的数据
  • 提供商提供的数据取代消费者提供的默认值
  • 数据的语义永远不会改变;相反,你从模式中删除旧的含义(消费者依赖于它将回归到他们的默认理解)并添加一个新的含义(现有的消费者会忽略)。

还有schema evolution上的Oracle参考,其中描述了“您可以安全地对您的架构执行的修改,而不会有任何顾虑。”它引用了这些相同的规则。

Avro的详细信息schema migration rules描述了生产者架构和消费者架构的解析方式。相同的基本想法,更多关于更改数据类型的细节。

RFC 4287描述了Atom Syndication中的“必须忽略外部标记”要求。 Versioning XML Vocabularies包括对必须忽略的良好讨论。

请注意,我们真正谈论的是处理模型,David Megginson points out。兼容性保证是采用正确的处理模型的结果。

  

你是否应该删除信封

信封对我来说似乎是浪费时间。添加值为state的字段与添加保存记录的新字段之间没有任何有用的区别。

message.get('foo').orElse('baz')

message.get('data').get('foo').orElse('baz')
抱歉,我无法区分。看起来像自行车脱落。

如果用具有语义意义的记录替换泛型包络,并使用一个理解它可能无法理解消息的所有部分的处理模型,那么它非常强大(无论是否使用顶级字段或记录)。

另外,请注意,如果您处于管道中间,“必须忽略”表示忽略,而不是扔掉 - 您需要将整个消息传递到下一个阶段管道,并允许它自己决定应用哪些处理规则。

  

所以,我对你的评论的看法是:如果信封具有语义意义,那就没问题。你能举个例子吗?

因此,从我的角度来看,没有充分理由选择以下任何一种

get[xyzzy]
get[data][xyzzy]
get[data/xyzzy]

因为“数据”在任何一种拼写中都不会告诉处理引擎任何有用的东西。

另一方面,将消息分解为消费者可能关心的独立逻辑部分是有趣的。考虑事件HTTPRequestReceived - 这样的消息可能是什么样的?显然,我们关注事件本身(以便观察者可以评估他们对消息的兴趣),以及关于记录内容的数据,数据,可能是对人类或机器人流量的评估,来源事件......

{ event : { type : HttpRequestReceived }
, source : { logFile : ..., position : ... }
, rawData : [ ... ]
, botAnalysis : { ... }
}
  

我可以轻松地使用URI作为命名空间。

你可以。性感变体是将标识符用作键,用于查找模式定义

{ http://example.org/event : 
    { http://example.org/event/type : HttpRequestReceived }
....

Schema.org有一个用于重复使用的常用概念词汇

{ http://schema.org/dateCreated : 2017-02-13 }