Http4s Client Encode Entity как x-www-form-urlencoded Рекурсивно

У меня есть такая просьба

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 со всеми различными конечными точками / запросами, я хотел бы найти способ кодировать данный класс case как форму. Как лучше всего это сделать?

Я пытался:

  1. Явное указание Content-Type но это не работает, потому что унаследованный тип имеет приоритет

  2. Создание неявного универсального преобразования из Product к 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 но Цирцея не может определиться с правильным KeyEncoder

Как правильно подойти к этому, чтобы данный Product закодировано по потоку?

1 ответ

I just faced the same issue and this is the way it worked for me.

From 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")
    )

And that's it. For some strange reason using .withHeaders didn't work for me, seems like they are overridden or so.

Другие вопросы по тегам