3

我有一个控制器,我想像这样接受 JSON 的创建操作:

{ "foo": "bar" }

或者像这样:

{ "widget": { "foo": "bar" } }

也就是说,我想接受包含在包含对象中的小部件或小部件。目前,我的控制器的创建操作看起来很像这样:

func createHandler(_ req: Request) throws -> Future<Widget> {
  do {
    return try req.content.decode(WidgetCreateHolder.self).flatMap(to: Widget.self) {
      return createWidget(from: $0.widget)
    }
  } catch DecodingError.keyNotFound {
    return try req.content.decode(WidgetCreateObject.self).flatMap(to: Widget.self) {
      return createWidget(from: $0)
    }
  }
}

WidgetCreateObject看起来像:

struct WidgetCreateObject { var foo: String? }

WidgetCreateHolder看起来像:

struct WidgetCreateHolder { var widget: WidgetCreateObject }

也就是说,我的 create 操作应该try创建一个持有者,但如果失败,它应该捕获错误并尝试创建内部对象 (a WidgetCreateObject)。但是,当我将此代码部署到 Heroku 并仅使用内部对象 JSON 发出请求时,我在日志中得到了这个:

[ ERROR ] DecodingError.keyNotFound: Value required for key 'widget'. (ErrorMiddleware.swift:26)

即使我试图抓住那个错误!

如何让我的创建操作接受两种不同格式的 JSON 对象?

4

1 回答 1

2

弄清楚了!

decode方法返回 a Future,以便稍后发生实际解码(以及因此错误),而不是在 do/catch 期间。这意味着没有办法用这个 do catch 来捕捉错误。

幸运的是,Futures 有一系列以catch;开头的方法。我感兴趣的是catchFlatMap,它接受来自 的闭包Error -> Future<Decodable>。此方法“捕获”在调用的 Future 中抛出的错误,并将错误传递给闭包,使用任何下游 future 中的结果。

所以我能够将我的代码更改为:

func createHandler(_ req: Request) throws -> Future<Widget> {
    return try req.content.decode(WidgetCreateHolder.self).catchFlatMap({ _ in
        return try req.content.decode(WidgetCreateObject.self).map(to: WidgetCreateHolder.self) {
            return WidgetCreateHolder(widget: $0)
        }
    }).flatMap(to: Widget.self) {
        return createWidget(from: $0.widget)
    }
}
于 2020-02-04T20:36:46.253 回答