QueryStringBinder for List[String]

2019-02-07 09:16发布

Using Play 2.0.1 I defined the following route:

GET  /demo/list controllers.Demos.listDemos(page: Int ?= 0, orderBy: Int ?= 1, nameFilter: String ?= "", versionFilter: Long ?= -1, tagFilter: List[String] ?= Nil)

But I get this error on compile:

No QueryString binder found for type List[String]. Try to implement an implicit QueryStringBindable for this type.

I found the code from Play 2.1-RC (not officially released yet) which would solve the issue:

/**
* QueryString binder for List
*/
  implicit def bindableList[T: QueryStringBindable] = new QueryStringBindable[List[T]] {
    def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params)))
    def unbind(key: String, values: List[T]) = unbindList(key, values)
  }

  /**
* QueryString binder for java.util.List
*/
  implicit def bindableJavaList[T: QueryStringBindable] = new QueryStringBindable[java.util.List[T]] {
    def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params).asJava))
    def unbind(key: String, values: java.util.List[T]) = unbindList(key, values.asScala)
  }

  private def bindList[T: QueryStringBindable](key: String, params: Map[String, Seq[String]]): List[T] = {
    for {
      values <- params.get(key).toList
      rawValue <- values
      bound <- implicitly[QueryStringBindable[T]].bind(key, Map(key -> Seq(rawValue)))
      value <- bound.right.toOption
    } yield value
  }

  private def unbindList[T: QueryStringBindable](key: String, values: Iterable[T]): String = {
    (for (value <- values) yield {
      implicitly[QueryStringBindable[T]].unbind(key, value)
    }).mkString("&")
  }

But after a couple of hours trying, Play still doesn't find the code. I've tried using a custom object and implicit conversions to that object,but it still doesn't work.

How can I force Play to do the implicit conversion?

Update

I want to avoid 2.1-SNAPSHOT if possible as I'm concerned about its stability, but I will use it if there is no other way. I tried the solution from 4e6 but it did not work:

[warn]  ::::::::::::::::::::::::::::::::::::::::::::::                                 
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.slf4j#slf4j-api;1.6.1: configuration not found in org.slf4j#slf4j-api;1.6.1: 'compile'. It was required from org.hibernate#hibernate-validator;4.2.0.Final compile
[warn]  :: commons-codec#commons-codec;1.4: configuration not found in commons-codec#commons-codec;1.4: 'compile'. It was required from org.apache.httpcomponents#httpclient;4.1.2 compile
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::

My sbt plugin contains this:

// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository
resolvers ++= Seq(
  Resolver.url("Typesafe Ivy Snapshots", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns),
  "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/",
  "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
  "DefaultMavenRepository" at "http://repo1.maven.org/maven2/",
  "JavaNet1Repository" at "http://download.java.net/maven/1/")

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.1-SNAPSHOT")

2条回答
三岁会撩人
2楼-- · 2019-02-07 09:19

You can move your project to play 2.1-SNAPSHOT instead (see "Create a new application without having Play installed" section of documentation). All you need is to change sbt-plugin version and add extra resolvers. Here is my version of plugins.sbt

resolvers ++= Seq(
  Resolver.url("Typesafe Ivy Snapshots", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns),
  "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/",
  "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
  "Codahale Repo" at "http://repo.codahale.com")

addSbtPlugin("play" % "sbt-plugin" % "2.1-SNAPSHOT")

Update

I don't know why you have problem with slf4j dependency. It is successfully downloaded from typesafe releases for me. You can try to explicitly add this repository to your application config (I mean Build.scala, not plugins.sbt).

[info] downloading http://repo.typesafe.com/typesafe/releases/org/slf4j/slf4j-api/1.6.1/slf4j-api-1.6.1.jar ...
[info]  [SUCCESSFUL ] org.slf4j#slf4j-api;1.6.1!slf4j-api.jar (538ms)
查看更多
叛逆
3楼-- · 2019-02-07 09:23

I solved it at last. The code used is:

Under models, file QueryBinders.scala:

package models

import play.api.mvc.{JavascriptLitteral, QueryStringBindable}


/**
 * Created with IntelliJ IDEA.
 * User: pvillega
 * Date: 07/05/12
 * Time: 12:06
 * QueryStringBinders for some data types missing in 2.0.1
 */
//TODO: remove when updating to 2.1
object QueryBinders {

  /**
   * QueryString binder for List
   */
  implicit def bindableList[T: QueryStringBindable] = new QueryStringBindable[List[T]] {
    def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params)))
    def unbind(key: String, values: List[T]) = unbindList(key, values)
  }

  private def bindList[T: QueryStringBindable](key: String, params: Map[String, Seq[String]]): List[T] = {
    for {
      values <- params.get(key).toList
      rawValue <- values
      bound <- implicitly[QueryStringBindable[T]].bind(key, Map(key -> Seq(rawValue)))
      value <- bound.right.toOption
    } yield value
  }

  private def unbindList[T: QueryStringBindable](key: String, values: Iterable[T]): String = {
    (for (value <- values) yield {
      implicitly[QueryStringBindable[T]].unbind(key, value)
    }).mkString("&")
  }

  /**
   * Convert a Scala List[T] to Javascript array
   */
  implicit def litteralOption[T](implicit jsl: JavascriptLitteral[T]) = new JavascriptLitteral[List[T]] {
    def to(value: List[T]) = "[" + value.map { v => jsl.to(v)+"," } +"]"
  }

}

On Build.scala:

 val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here
      lessEntryPoints <<= baseDirectory(_ / "app" / "assets" / "stylesheets" ** "style.less"),
      routesImport += "models.QueryBinders"
    )

This will have to be removed when updating to 2.1, but it works and it avoids all the issues I had trying to update the project to 2.1 Snapshot.

查看更多
登录 后发表回答