问题描述
我想在我的 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/1
或 b/c
等) 且分数应小于 1(1/2
、5/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'")
}
}