用于复杂验证的scala提取器模式,但具有良好的错误输出

时间:2014-10-13 16:24:30

标签: scala scala-2.10

我正在努力在特定用例中使用提取器模式,似乎它可能非常强大。

我从Web请求的Map [String,String]输入开始。这是对我们的api的searchRequest或countRequest。

searchRequest有密钥

  • 查询(必需)
  • FROM日期(可选-默认的)
  • TODATE(可选-默认的)
  • 的nextToken(可选)
  • 的maxResults(可选-默认的)

countRequest有键

  • 查询(必需)
  • FROM日期(可选-默认的)
  • TODATE(可选-默认的)
  • 桶(可选-默认的)

然后,我想将这两者转换为像这样的组合类型结构

protected case class CommonQueryRequest(
  originalQuery: String,
  fromDate: DateTime,
  toDate: DateTime
)

case class SearchQueryRequest(
  commonRequest: CommonQueryRequest,
  maxResults: Int,
  nextToken: Option[Long])

case class CountRequest(commonRequest: CommonQueryRequest, bucket: String)

正如你所看到的,我将字符串转换为DateTimes和Int,Long等等。我的问题是我真的需要将无效的fromDate与无效的toDate格式与无效的maxResults与无效的下一个令牌IF可用相关的错误

与此同时,我需要坚持默认值(根据搜索或计数请求的不同而不同)。

当然,随着Map的传入,你可以告诉搜索与计数,所以在我第一次看到这个时,我添加了一个key =“type”,其值为search或count,这样我至少可以匹配

我甚至走错了路吗?我想也许使用匹配可能比我们现有的实现更清晰,但是我走得更远,这看起来有点丑陋。

感谢, 迪安

2 个答案:

答案 0 :(得分:2)

我建议你看看scalaz.Validation和ValidationNel。它是收集验证错误的超级好方法,非常适合输入请求验证。

您可以在此处详细了解验证:http://eed3si9n.com/learning-scalaz/Validation.html。但是在我的示例中,我使用的是scalaz 7.1,它可能与本文中描述的略有不同。然而,主要观点仍然相同。

以下是用例的小例子:

  import java.util.NoSuchElementException

  import org.joda.time.DateTime
  import org.joda.time.format.DateTimeFormat

  import scala.util.Try

  import scalaz.ValidationNel
  import scalaz.syntax.applicative._
  import scalaz.syntax.validation._

  type Input = Map[String, String]
  type Error = String

  case class CommonQueryRequest(originalQuery: String,
                                fromDate: DateTime,
                                toDate: DateTime)

  case class SearchQueryRequest(commonRequest: CommonQueryRequest,
                                maxResults: Int,
                                nextToken: Option[Long])

  case class CountRequest(commonRequest: CommonQueryRequest, bucket: String)

  def stringField(field: String)(input: Input): ValidationNel[Error, String] =
    input.get(field) match {
      case None => s"Field $field is not defined".failureNel
      case Some(value) => value.successNel
    }


  val dateTimeFormat = DateTimeFormat.fullTime()

  def dateTimeField(field: String)(input: Input): ValidationNel[Error, DateTime] =
    Try(dateTimeFormat.parseDateTime(input(field))) recover {
      case _: NoSuchElementException => DateTime.now()
    }  match {
      case scala.util.Success(dt) => dt.successNel
      case scala.util.Failure(err) => err.toString.failureNel
    }

  def intField(field: String)(input: Input): ValidationNel[Error, Int] =
    Try(input(field).toInt) match {
      case scala.util.Success(i) => i.successNel
      case scala.util.Failure(err) => err.toString.failureNel
    }

  def countRequest(input: Input): ValidationNel[Error, CountRequest] =
    (
      stringField  ("query")   (input) |@|
      dateTimeField("fromDate")(input) |@|
      dateTimeField("toDate")  (input) |@|
      stringField  ("bucket")  (input)
    ) { (query, from, to, bucket) =>
        CountRequest(CommonQueryRequest(query, from, to), bucket)
    }


  val validCountReq = Map("query" -> "a", "bucket" -> "c")
  val badCountReq = Map("fromDate" -> "invalid format", "bucket" -> "c")

  println(countRequest(validCountReq))
  println(countRequest(badCountReq))

答案 1 :(得分:0)

scalactic看起来也很酷,我可能会走那条路(虽然不确定我们是否可以使用lib,但我想我会继续前进,直到有人说不。)