Play 在单独的包中找不到自定义 QueryString 活页夹

Play cannot find custom QueryString binder in separate package

尝试为 models.DeviceContext 实现我的第一个 QueryStringBindable:

case class DeviceContext( deviceIdLike: String = "", deviceUseridLike: String = "") 

我有util.Binders.scala如下:

package util

import play.api.mvc.QueryStringBindable
import models._

object Binders {

  implicit def deviceContextBinder(implicit stringBinder: QueryStringBindable[String]) =
    new QueryStringBindable[DeviceContext] {

      override def bind(key: String, params: java.util.Map[String, Array[String]]): Option[Either[String, DeviceContext]] = {
        Some({
          val deviceIdLike = stringBinder.bind(key + ".deviceIdLike", params)
          val deviceIUseridLike = stringBinder.bind(key + ".deviceUseridLike", params)
          (deviceIdLike.isDefined && deviceIUseridLike.isDefined) match {
            case true => Right(DeviceContext(deviceIdLike.get, deviceIUseridLike.get))
            case false => Left("Unable to bind DeviceContext")
          }
        })
      }

      override def unbind(key: String, deviceContext: DeviceContext): String =
        stringBinder.unbind(
          key + ".deviceIdLike=" + deviceContext.deviceIdLike +
            "&" + key + ".deviceUserIdLike=" + deviceContext.deviceUseridLike)
    }

}

改编自 documentation 中的示例。我的 build.sbt 文件试图使 util.Binders 对路由可见,其中:

import play.PlayImport.PlayKeys._
routesImport += "util.Binders._"

路线看起来像:

GET  /device controllers.Devices.list(dc:models.DeviceContext)

然而,编译器并不高兴:

[error] \conf\routes:76: No QueryString binder found for type models.DeviceContext. Try to implement an implicit QueryStringBindable for this type. [error] GET /device controllers.Devices.list(deviceContext:DeviceContext)

[error] \conf\routes:76: not enough arguments for method implicitly: (implicit e: play.api.mvc.QueryStringBindable[models.DeviceContext])play.api.mvc.QueryStringBindable[models.DeviceContext]. [error] Unspecified value parameter e.

Scala版本是2.11.5,play是2.3.9

import util.Binders 是显式导入,因此应包含在隐式搜索中。就好像 implicit def deviceContextBinder 的签名不符合要求,但它 returns 是我的类型的 QueryStringBindable,所以我不明白为什么它不匹配。

感谢任何指点!

(附带说明,文档使用 params: Map[String,Seq[String]] 但如果我使用它,编译器会抱怨它期望 java.util.Map[String,Array[String]] 这看起来很奇怪......请参阅下面的答案。)

编辑 1: 当我使用标准数据类型时,为了不调用我的客户QueryStringBindable,而是将代码留在项目中,编译器随后报告新的错误,第一个错误是:

type arguments [String] do not conform to trait QueryStringBindable's type parameter bounds [T <: play.mvc.QueryStringBindable[T]]

而不是 routesImport += "util.Binders" 尝试 routesImport += "util.Binders._" 从对象中导入所有内容。

好吧,解决方案是我似乎必须将代码放在与案例 class 相同的包中。当我这样做时,上面提到的 java.util.Map "workaround" 解决了,因此,删除 Binders.scala 并将 DeviceContext.Scala 扩展到以下工作:

package models

case class DeviceContext(deviceIdLike: String = "", deviceUseridLike: String = "")

object DeviceContext {
  import play.api.mvc.QueryStringBindable

  implicit def deviceContextBinder(implicit stringBinder: QueryStringBindable[String]) =
    new QueryStringBindable[DeviceContext] {

      override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, DeviceContext]] = {
        Some({
          val deviceIdLike = stringBinder.bind(key + ".deviceIdLike", params)
          val deviceIUseridLike = stringBinder.bind(key + ".deviceUseridLike", params)
          (deviceIdLike, deviceIUseridLike) match {
            case (Some(Right(di)), Some(Right(du))) => Right(DeviceContext(di, du))
            case _ => Left("Unable to bind DeviceContext")
          }
        })
      }

      override def unbind(key: String, deviceContext: DeviceContext): String =
        stringBinder.unbind(
          key + ".deviceIdLike", deviceContext.deviceIdLike) +
          "&" + stringBinder.unbind(key + ".deviceUserIdLike", deviceContext.deviceUseridLike)
    }

}

我还发现,如果我想在路由文件中以非限定方式引用我的类型,则 需要 routesImports,即 controllers.Devices.list(dc:DeviceContext)。如果我愿意把参数写成dc:models.DeviceContext,那么就不需要routesImports了。

在我的例子中,我在错误的伴随对象中定义了 QueryStringBindable。