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}"
}
}
}
对于我的一条路线,我有一个可选参数,即 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}"
}
}
}