Scala Play:使用正则表达式路由可选参数?

Scala Play: Routes optional parameter with regex?

对于我的一条路线,我有一个可选参数,即 birthDate: Option[String] 并且可以这样做:

GET /rest/api/findSomeone/:firstName/:lastName controllers.PeopleController.findSomeone(firstName: String, lastName: String, birthDate: Option[String])

但是,为了更严格地使用 birthDate 可选参数,指定这样的正则表达式会很有帮助:

$birthDate<([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))>

但是因为这是一个可选参数,所以我找不到实现它的方法.. 这在 Play 2 中有介绍。7.x?我面临着使 birthDate 参数成为非可选参数或不选中它的困境。

作为旁注。我一直在尝试整合 Joda 时间的路由绑定,例如org.joda.time.LocalDate 通过添加以下依赖项 https://github.com/tototoshi/play-joda-routes-binder "com.github.tototoshi" %% "play-joda-routes-binder" % "1.3.0" 但它在我的项目中不起作用,因为我在集成它后遇到编译错误所以我暂时隐藏了这种方法。

对于解析日期,我根本不建议使用基于正则表达式的验证器。相反,您可以 - 例如 - 使用自定义案例 class 和查询字符串绑定器,它将对传入参数进行 type-safe 解析:

package models

import java.time.LocalDate
import java.time.format.{DateTimeFormatter, DateTimeParseException}

import play.api.mvc.QueryStringBindable

case class BirthDate(date: LocalDate)

object BirthDate {
  private val dateFormatter: DateTimeFormatter = DateTimeFormatter.ISO_DATE // or whatever date format you're using

  implicit val queryStringBindable = new QueryStringBindable[BirthDate] {
    override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, BirthDate]] = {
      params.get(key).flatMap(_.headOption).map { value =>
        try {
          Right(BirthDate(LocalDate.parse(value, dateFormatter)))
        } catch {
          case _: DateTimeParseException => Left(s"$value cannot be parsed as a date!")
        }
      }
    }

    override def unbind(key: String, value: BirthDate): String = {
      s"$key=${value.date.format(dateFormatter)}"
    }
  }
}

现在,如果您更改路由配置,使 birthDate 成为 Option[BirthDate] 类型的参数,您将获得所需的行为。

如果您坚持使用正则表达式,您可以使用 regex-based 解析器代替日期格式化程序,并让 BirthDate 包装 String 而不是 LocalDate,但对于所提供的用例,我真的看不出这样做有什么好处。

编辑:为了完整起见,regex-based 变体:

case class BirthDate(date: String)

object BirthDate {
  private val regex = "([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))".r

  implicit val queryStringBindable = new QueryStringBindable[BirthDate] {
    override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, BirthDate]] = {
      params.get(key).flatMap(_.headOption).map { value =>
        regex.findFirstIn(value).map(BirthDate.apply).toRight(s"$value cannot be parsed as a date!")
      }
    }

    override def unbind(key: String, value: BirthDate): String = {
      s"$key=${value.date}"
    }
  }
}