Play框架2:如何在路由,视图和控制器之间传递对象?

时间:2014-04-03 08:32:40

标签: scala playframework-2.0

我正在尝试将书籍对象从视图传递到路径,然后将其发送到控制器中进行计算。我的代码如下:

bookList.scala.html

@(books: java.lang.Iterable[Book])

@main("BookList"){
    <div class="row">
        @for(book <- books.iterator()){
            <div class="col-sm-6 col-md-4">
                <div class="thumbnail" style="height: 435px">
                         ...
                        <p><a href="@routes.Application.buy(book)" class="btn btn-primary" role="button"
                              style="vertical-align:bottom">Order now!</a>
                    </div>
                </div>
            </div>
        }
    </div>

}

路由

...
GET     /order                      controllers.Application.buy(book: models.Book)
...

然而,它给了我一个错误:No QueryString binder found for type models.Book. Try to implement an implicit QueryStringBindable for this type.

我尝试将路线更改为:

    GET     /order                      controllers.Application.buy(book)

它还返回了一个错误:

type mismatch; found : String required: models.Book

4 个答案:

答案 0 :(得分:9)

这不是播放路由的工作原理。 Play路由器解析URL或查询字符串中的变量,并通过QueryBindable类型类将它们转换为本机类型。你应该有更多这样的东西:

<强>路由

GET /order/:bookid           controllers.Application.buy(bookid: String)

行动应该是:

<强> Application.scala

def buy(bookid: String) = Action { request =>
    // Look up book by id here.
   Ok(views.html.thanks("you bought a book!"))
}

这样的模板:

<强> bookList.scala.html

@for(book <- books.iterator()) {
    ...
    <a href="@routes.Application.buy(book.id)" class="btn btn-primary"         
}

当然,如果你的模型的ID不是String,你需要修改路线的参数类型

更新 - 使用表格/ POST替代

使用POST方法的表单是更好的解决方案,或者用户每次单击URL时都会购买另一本书,并且ID将被公开。查看forms文档。你的模板是这样的:

@for(book <- books.iterator()) {
    ...
    <form method="post">
      <div>@book.name</div>
      <input type="hidden" value="@book.id"/><button type="submit">buy</button>
    </form>
}

答案 1 :(得分:4)

您不能简单地将对象(bean)作为url查询参数传递。

默认您只能将简单类型定义为参数类型。请仔细阅读play的rounting文档 - &gt; http://www.playframework.com/documentation/2.2.x/ScalaRouting尤其是参数类型

但是play框架有可能“学习”如何将特定的url解释为bean。 你发布了错误的女巫信息。负责此 QueryStringBindable - &gt; http://www.playframework.com/documentation/2.2.1/api/java/play/mvc/QueryStringBindable.html

简而言之,就像在文档中一样,当你定义这样的类时:

class Book implements QueryStringBindable<Book> {
     public String title;
     public int numpages;

     public Option<Pager> bind(String key, Map<String, String[]> data) {
         if (data.contains(key + ".title" && data.contains(key + ".numpages") {
             try {
                 title = data.get(key + ".title")[0];
                 numpages = Integer.parseInt(data.get(key + ".numpages")[0]);
                 return Some(this);
             } catch (NumberFormatException e) {
                 return None();
             }
         } else {
             return None();
         }
     }

     public String unbind(String key) {
         return key + ".title=" + title + "&" + key + ".numpages=" + numpages;
     }

     public String javascriptUnbind() {
         return "function(k,v) {\n" +
             "    return encodeURIComponent(k+'.title')+'='+v.title+'&'+encodeURIComponent(k+'.numpages')+'='+v.numpages;\n" +
             "}";
     }
 }

然后你可以定义路线,如:

GET  /order     controllers.Application.buy(p: Book)

然后您可以在浏览器中运行,例如链接:

localhost:9000/?p.title=SomeTitle&p.numpages=235

购买控制器中,您将获得p参数作为Book类实例。

我没有测试这段代码,这是在java中。但你应该明白这一点。

答案 2 :(得分:0)

您可以定义自定义PathBindable以自动将id转换为book对象,并将其传递给controller中的buy方法。请参阅PathBindableanother example

注意:我会在与Book模型相同的文件中定义PathBindable。

答案 3 :(得分:0)

我曾经使用PathBindable在UUID中解析的简单示例。

我不必解析UUID并确保它们在所有控制器/操作代码中都很好地形成,所以我在路由器级别使用它。我使用UUIDP(P for param)来不污染UUID名称空间。

案例类和分析器

case class UUIDP(key: String, value: String, uuid: UUID)

object UUIDP {
  implicit def pathBinder(implicit intBinder: PathBindable[String]) = new PathBindable[UUIDP] {

    val uuidRegex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})".r

    override def bind(key: String, value: String): Either[String, UUIDP] = {

      value match {
        // sanity check, prevent errors, is this really a UUID?
        case uuidRegex(c) => {
          //error check too, binary value can be invalid
          try { Right(UUIDP(key,value,UUID.fromString(value))) }
          catch {
            case ex : IllegalArgumentException => Left("Unparsable UUID") 
          }
        }
        case _ => Left("Invalid UUID Format") 
      }
    }

    // in case we need to remove this from the request
    override def unbind(key: String, user: UUIDP): String = {
      intBinder.unbind(key, user.value)
    }
  }
}

CONF /路由

GET /items/:id @com.foo.MyController.getItem(id: UUIDP)

控制器

def getItem(sessionId: UUIDP) = Action(parse.text) { request =>
  Ok(s"Fetching item [${sessionId.uuid}]")
}