为播放框架 http 响应创建 Writeable[Argonaut.Json]

creating Writeable[Argonaut.Json] for play framework http response

我正在尝试通过像这样使用 plays json 库来更改此功能的实现

def apply[T](action: => ApiResponse[T])(implicit tjs: Writes[T], ec: ExecutionContext): Future[Result] = {
    action.fold(
      err =>
        Status(err.statusCode) {
          JsObject(Seq(
            "status" -> JsString("error"),
            "statusCode" -> JsNumber(err.statusCode),
            "errors" -> Json.toJson(err.errors)
          ))
        },
      t =>
        Ok {
          JsObject(Seq(
            "status" -> JsString("ok"),
            "response" -> Json.toJson(t)
          ))
        }
    )
  }

像这样使用 argonaut

def apply[T](action: => ApiResponse[T])(implicit encodeJson: EncodeJson[T], ec: ExecutionContext): Future[Result] = {
    action.fold(
      err =>
        Status(err.statusCode) {
          Json(
          "status" -> jString("error"),
          "statusCode" -> jNumber(err.statusCode),
          "errors" -> err.errors.asJson
          )
        },
      t =>
        Ok {
          Json(
          "status" -> jString("ok"),
          "response" -> t.asJson
          )
        }
    )
  }

但我得到

Cannot write an instance of argonaut.Json to HTTP response. Try to define a Writeable[argonaut.Json]

对于 Status{} 块和 Ok{} 块,我在这里得到了这个问题的有用答案 https://groups.google.com/forum/#!topic/play-framework/vBMf72a10Zc

所以我尝试像这样创建隐式转换

implicit def writeableOfArgonautJson(implicit codec: Codec): Writeable[Json] = {
      Writeable(jsval => codec.encode(jsval.toString))
    }

我认为将 json 对象转换为字符串并将其提供给 codec.encode 应该将其转换为 Array[Bytes] 但我得到

Cannot guess the content type to use for argonaut.Json. Try to define a ContentTypeOf[argonaut.Json]

jsval.nospaces.getBytes 也 return Array[Bytes] 所以我不知道这是否可以用来提供帮助

所以虽然我认为最后一条错误消息意味着我只需要告诉播放它应该使用内容类型 application.json 我也觉得这可能是一个不必要的兔子洞,应该有一个更简单的方法来做这个。

编辑:它不是像定义 contentType 这样的兔子洞,至少可以编译,但我仍然想知道这是否正确

您似乎已经回答了您自己的问题,但为了确认,Writable[A] 是:

  1. 如何将类型 A 转换为 Array[Bytes]
  2. 在响应中使用什么内容类型,这也需要
  3. 当前字符编码

字符编码由隐式 Codec 实例负责,因此您需要一个隐式 ContentTypeOf[A],其中 Aargonaunt.Json:

implicit def contentTypeOf_ArgonautJson(implicit codec: Codec): ContentTypeOf[argonaut.Json] = {
  ContentTypeOf[argonaut.Json](Some(ContentTypes.JSON))
}

然后是 Writable[A],它在 A 上有一个 type constraint,还有一个 in-scope ContentTypeOf[A](您刚刚定义) :

implicit def writeableOf_ArgonautJson(implicit codec: Codec): Writeable[argonaut.Json] = {
  Writeable(jsval => codec.encode(jsval.toString))
}

正如您所指出的,兔子洞到此为止。事实上,当你认为你现在可以在没有进一步转换和 header-setting 样板的情况下,你可以在任意多的操作中执行 Ok(myArgonautObject) 时,它确实看起来有点转移但没有太多额外的代码。

也许您可以将这些隐含内容放入 ExtraJsonHelpers 特征中,并将其混合到您的控制器中,以减少更多的样板文件。