在 Scala 中使用 QueryParamDecoder 解码一个可选的查询参数

问题描述

我想在我的 Scala 代码中解码一个可选的查询参数。我正在使用 http4s。参数的形式为 ?part=35/43。最终目标是将此分数存储为 Type Part = (Int,Int),以便我们可以将 (35,43) 作为元组在代码中进一步使用。我创建了一个对象,如: https://http4s.org/v0.18/dsl/#optional-query-parameters

  object OptionalPartitionQueryParamMatcher
      extends OptionalValidatingQueryParamDecoderMatcher[Part]("part")

现在 OptionalValidatingQueryParamDecoderMatcher 需要一个隐含的 QueryParamDecoder[Part] 作用域。

为此我创建了一个隐式 val,它需要检查我们是否真的有一个有效的分数,也就是说,两个字符都应该是一个数字(而不是 a/1b/c 等) 且分数应小于 1(1/25/8 等):

  implicit val ev: QueryParamDecoder[Part] =
    new QueryParamDecoder[Part] {
      def decode(
          partition: QueryParameterValue
        ): ValidatedNel[ParseFailure,Part] = {

        val partAndPartitions = partition.value.split("/")

        Validated
          .catchOnly[NumberFormatException] {
            val part = partAndPartitions(0).toInt
            val partitions = partAndPartitions(1).toInt

            if (
              partAndPartitions.length != 2 || part > partitions || part <= 0 || partitions <= 0
            ) {
              throw new IllegalArgumentException
            }
            (part,partitions)
          }
          .leftMap(e => ParseFailure("Invalid query parameter part",e.getMessage))
          .toValidatedNel
      }
    }

上面代码的问题是,它只捕获 NumberFormatException(当它无法使用 .toInt 将字符串转换为 Int 时也是如此)但是如果我输入类似 ?part=/1 的东西怎么办,然后它应该捕获 ArrayIndexOutOfBoundsException 因为我正在查询数组中的前两个值,或者当分数根本无效时让我们说 IllegalArgumentException 。我怎样才能做到这一点,在一次通过中捕获所有内容?谢谢!

解决方法

好吧,最简单的方法是使用 .catchOnly[Throwable] (甚至直接使用 QueryParamDecoder .fromUnsafeCast,它会捕获任何错误。

但是,我个人更愿意做这样的事情:
(我现在无法编译代码,如有错别字请见谅)

implicit final val PartQueryParamDecoder: QueryParamDecoder[Part] =
  QueryParamDecoder[String].emap { str =>
    def failure(details: String): Either[ParseFailure,Part] =
      Left(ParseFailure(
        sanitized = "Invalid query parameter part",details = s"'${str}' is not properly formttated: ${details}"
      ))

    str.split('/').toList match {
      case aRaw :: bRaw :: Nil =>
        (aRaw.toIntOption,bRaw.toIntOption) match {
          case (Some(a),Some(b)) =>
            Right(Part(a,b))

          case _ =>
            failure(details = "Some of the fraction parts are not numbers")
        }

      case _ =>
        failure(details = "It doesn't correspond to a fraction 'a/b'")
    }
  }

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...