将多个可能的数据输入合并到同一个 REST 端点通常是个坏主意,这会使 REST 的整个点无效。
如果可能的话,我会为不同的数据模型创建一个单独的路由,并让客户端根据导致其产生不同数据负载的任何条件调用不同的端点。
POST /api/v1/process/a controllers.Bla.doStuffA()
POST /api/v1/process/b controllers.Bla.doStuffB()
然后,您可以在后端拥有数据模型的 ADT(如有必要),并将您的路由逻辑专门用于该 ADT 的叶子,在幕后对该 ADT 的任何部分使用相同的服务方法。
trait ContentModel
case class ContentModelA(....) extends ContentModel
case class ContentModelB(...) extends ContentModel
现在你也许可以:
def processModel(data: ContentModel) = {
data match {
case ContentModelA(...) =>
case ContentModelB(...) =>
}
}
所以你的 doStuffA 和 doStuffB 控制器方法看起来像:
def doStuffA = Action.async { implicit req => req.body.asJson.map(_.validate[ContentModelA].map(processData) }
def doStuffB = Action.async { implicit req => req.body.asJson.map(_.validate[ContentModelB].map(processData) }
如果必须的话
从最严格的意义上回答您的问题:
val jsonOpt = request.body.asJson
jsonOpt map { payload =>
payload.validate[ContentModelA] match {
case JsSuccess() => ... we have an A Model
case JsError(err, paths) =>
// couldn't deal with A, let's try B
payload.validate[ContentModelB] match {
case JsSuccess() => //we have a B
case JsError(err, paths) => // oops, neither A nor B.
}
}
}
你可以用一个非常漂亮的 Shapeless 副产品来做到这一点,并为副产品中的所有类型提供格式证明,然后从那里开始,但我认为这会把它吹出公园。