5

我有如下要求

val request =
    Request[IO](
      method = POST,
      uri = Uri.uri("..."),
      headers = Headers(
        Authorization(BasicCredentials("...", "..."))
      )
    )
    .withEntity(PaymentIntentRequest2(2000, "usd"))

我正在查看源代码,它看起来像withEntity继承了嵌套的标题,EntityDecoder所以上面的代码默认为Content-Type: application/json. 好像我明确地传递了UrlForm一切都很好。

不幸的是,我遇到的 API 期望数据为x-www-form-urlencoded,并且考虑到目标 API 的复杂性以及所有不同的端点/请求,我想找到一种将给定案例类编码为表单的方法。最好的方法是什么?

我努力了:

  1. 显式指定Content-Type但这不起作用,因为继承的类型优先

  2. Product构建从to的隐式泛型转换UrlForm(目前的扩展方法)

implicit class UrlFormEncode[+B <: Product](val u: B) {
    def asUrlForm: UrlForm =
      u.productElementNames
        .zip(u.productIterator)
        .foldLeft(UrlForm()) { (a, b) =>
          a.combine(UrlForm(b._1 -> b._2.toString))
        }
}

这里的问题是UrlForm在映射的两边都需要一个字符串。如果我只是用.toString它转换东西,因为嵌套类型例如:

ChargeRequest(Amount(refInt), EUR, source = Some(SourceId("...."))

导致以下json无效

{
  "currency": "EUR",
  "amount": "2000",
  "source": "Some(SourceId(....))",
  "customer": "None"
}

我试过asJson而不是,toString但circe无法决定正确的KeyEncoder

解决这个问题的正确方法是什么,以便将给定Product的内容编码到流中?

4

1 回答 1

0

我刚刚遇到了同样的问题,这就是它对我有用的方式。

来自https://http4s.org/v0.20/client/

// This import will add the right `apply` to the POST.
import org.http4s.client.dsl.io._


val form = UrlForm(
      OAuthAttribute.Code        -> code,
      OAuthAttribute.RedirectUri -> callbackUri,
      OAuthAttribute.GrantType   -> "authorization_code"
    )

private def buildRequest(tokenUri: Uri, form: UrlForm, header: String): Request[IO] =
    POST(
      form,
      tokenUri,
      Header.Raw(CIString("Authorization"), header),
      Header.Raw(CIString("Content-Type"), "application/x-www-form-urlencoded"),
      Header.Raw(CIString("Accept"), "application/json")
    )

就是这样。出于某种奇怪的原因,使用.withHeaders对我不起作用,似乎它们被覆盖了。

于 2021-09-14T14:50:46.250 回答