Play! Framework 2.3中bindFromRequest存在问题

9

我正在尝试使用Play的自动绑定功能,但一直没有成功。我在Eclipse 4.4 Luna上使用Java进行开发。

这是我的表单:

<h2>Create a new user</h2>
<form action="@routes.Backend.createUser()" method="post">
    First Name
    <input type="text" name="firstName" />
    Last Name
    <input type="text" name="lastName" />
    E-mail
    <input type="email" name="email" />
    PIN
    <input type="number" name="pin" />
    Status
    <input type="text" name="status" />
    Is guest?
    <input type="checkbox" name="isGuest" />

    <input type="submit" value="Create user" />
</form>

以下是我的 "Users" 类:

@Entity
public class Users extends Model {

// Database columns
@Id
public int userId;

public String firstName;
public String lastName;
public String email;
public int pin;
public String status;
public boolean isGuest;

}

这是我的控制器:

public class Backend extends Controller {

  public static Result createUser() {
    Form<Users> form = Form.form(Users.class).bindFromRequest();
    if (form.hasErrors()) {
        // doSomething()
    } else {
        Users u = form.get();
        u.save();
    }

    // TESTING
    // Checking the content of the request
    DynamicForm requestData = Form.form().bindFromRequest();
    String firstName = requestData.get("firstName");
    String lastName = requestData.get("lastName");
    // Printing the content works, I am able to see the correct values
    System.out.println(firstName); // Bob
    System.out.println(lastName); // Smith
    // This somehow doesn't work...
    System.out.println(u.firstName); // NULL
    System.out.println(u.lastName); // NULL
    System.out.println(u.userId); // Correctly generated
    // END OF TESTING

    return redirect(routes.Backend.allUsers());
  }
}

我想知道为什么自动绑定值不起作用。我已经确保我的表单字段名称与类中的属性名称对应,这应该足以使表单绑定工作,对吧?
我正在使用Eclipse Luna,并关闭了自动项目构建(我从控制台手动执行)。我知道有时候Eclipse可能会因为自动构建功能而出现问题。注意:这是正确的方法,但我没有使用activator命令清理项目,就像用户Dmitri建议的那样。此外,只要您不在Eclipse中打开自动构建功能,您只需要执行一次即可。 我已经尝试了多次重新启动Eclipse和应用程序,但都没有成功...
编辑: 我尝试仅使用Users类的String属性,因为requestData.get(String s)方法返回一个字符串。但仍然没有成功...
编辑2: 我将手动绑定值...如果有人有想法,请发帖 :)
编辑3: 我已更新我的代码,遵循下面答案中提到的规则。
编辑4: 只有在使用我的Postgresql 9.3数据库时,我才无法让自动绑定正常工作。当我使用内存数据库时,一切都顺利。此外,由于没有适用于Java 8和postgresql 9.3的JDBC驱动程序,我正在使用较旧版本的驱动程序(实际上驱动程序在PGSQL的网站上,但我无法在Play中让其正常工作)。我将不得不检查另一个DB会发生什么,然后我会在这里报告!
编辑5: 我尝试创建自定义数据绑定器,像这样:
        Formatters.register(User.class, new Formatters.SimpleFormatter<User>() {
        @Override
        public User parse(String arg0, Locale arg1) throws ParseException {
            User u = new Model.Finder<Integer, User>(Integer.class, User.class).byId(Integer.parseInt(arg0));
            return u;
        }
        @Override
        public String print(User arg0, Locale arg1) {
            return "User : " + arg0.firstName;
        }
    });

...但是它没有起作用!

编辑6: 用户Dmitri找到了一个可行的解决方案:你必须在Eclipse之外编译项目。看来Eclipse的编译器和Play!框架的编译器之间存在一些不兼容性...


你有收到任何错误吗? - blackbishop
绝对没有错误...我知道其余部分是工作的,因为我可以手动绑定值并创建我的用户对象实例,然后保存它并检索它,但这个自动绑定功能对我来说肯定是有问题的... - Danish Ashfaq
6个回答

12

我曾经遇到过完全相同的问题:bindFromRequest对于“name”字段返回null值。我按照这个Play for Java介绍视频中的人所做的一切(youtube.com/watch?v=bLrmnjPQsZc),但是仍然没有成功。

我使用Windows 7与JDK 1.8,并在Eclipse 4.4.0中工作,通过cygwin运行activator。

以下是解决我的问题的步骤:

  1. 在Eclipse中:Project -> Build Automatically - > 关闭
  2. 在cygwin中: ./activator clean; ./activator compile; ./activator run;

之后,bindFromRequest会正确地绑定name并将其放入数据库中。


我尝试创建自定义数据绑定器,但仍然无法正常工作,所以基本上我仍在努力解决这个问题...我准备放弃使用所有请求的普通SQL!但现在我会立即尝试并在此报告! - Danish Ashfaq
难以置信,它起作用了!所以这与 Eclipse 有关,对吧?你能解释一下究竟发生了什么吗?非常感谢! :) - Danish Ashfaq
没问题 :) 我怀疑这与编译后发生的ebean增强有关。Eclipse看到新的更改并再次编译,从而搞乱了代码。 - Dmitri Lihhatsov
很有可能。IntelliJ IDEA的用户没有报告过那种问题...我不知道是否已经提交了错误报告,应该向谁报告错误(Eclipse还是Play?),以及是否值得提交一个错误报告。无论如何,非常感谢您的帮助,否则我自己也找不出来! - Danish Ashfaq
...哇...令人惊叹..所以Eclipse IDE的问题是什么? - MADHAIYAN M

5

为您的数据模型创建getters/setters。这解决了我的问题。


这实际上是正确的答案。错误发生是因为Scala编译器添加了默认的getter和setter,而在使用Eclipse编译时没有被创建。 - Tom
即使今天,我仍然相信这是正确的答案,因为我曾经遇到过完全相同的问题。创建getter和setter可以解决这个问题。 - Ilhicas

2

您的代码中有:

Users u = Form.form(Users.class).bindFromRequest().get(); 

请试用这个替代方案:

Users user = new Users();
Form <Users> u = Form.form(Users.class).fill(user).bindFromRequest();

编辑:

可能问题出在您使用的输入类型上。尝试按照以下方式生成表单:

@form(routes.Backend.createUser()) {

            <label>First Name:</label> @inputText(userForm("first_name")) <br />
            <label>Last Name:</label> @inputText(userForm("first_name")) <br />
            <label>Email:</label> @inputText(userForm("email")) <br />
            <label>Pin:</label> @inputText(userForm("pin")) <br />
            <label>Status:</label> @inputText(userForm("status")) <br />
            <label>Is Guest:</label> @checkbox(userForm("is_guest")) <br />
            <input type="submit" value="Create user" />
    }

然后在 User Entity 中:尝试将所有列的类型更改为 String

@Entity
public class Users extends Model {

// Database columns
@Id
public int user_id;

public String first_name;
public String last_name;
public String email;
public String pin;
public String status;
public String is_guest;

}

在您的控制器中:
public class Backend extends Controller {

  public static Result createUser() {
    Form <Users> userForm = Form.form(Users.class).bindFromRequest();
    Users user = userForm .get();
    user.save();
}
}  

我在结尾处使用了“.get()”方法,它应该返回一个类型为“Users”的对象。这正是Java开发人员Play!框架介绍视频中所做的:https://www.youtube.com/watch?v=bLrmnjPQsZc - Danish Ashfaq
你的代码中的"userForm"是什么?我在Play框架的文档中查找了一下,但没有相关内容... 我只知道它是传递给模板的一个参数,例如@(userForm: Form[Users]),但它到底是什么呢? - Danish Ashfaq
请查看此链接:https://www.playframework.com/documentation/2.2.x/ScalaForms - blackbishop
好的,我假设只是一个简单的Form<Users> userForm = Form.form(Users.class); 我传递给我的模板。但我仍然无法让自动绑定工作...生成的表单很好,当我手动检查值时一切都正常。 - Danish Ashfaq
这是一个好的帖子 - blackbishop
感谢提供的信息。我已经成功地在另一个测试项目中按照视频教程的指示实现了自动绑定。我认为我的问题可能与示例中使用的简单数据库不同而更加复杂有关。我想目前我会手动绑定值,以后再研究。谢谢你的帮助 :) - Danish Ashfaq

1
我通过添加Setter和Getter解决了这个问题。 如果您有实体/模型类,应该添加Setter和Getter。 如果您有FormData类,请为其添加Setter和Getter。
因此,当您调用
Form<YourFormData> formData =       Form.form(YourFormData.class).bindFromRequest(); 
 YourFormData formData = formData.get(); 

您的formData现在将拥有所有设置的值。 希望这能帮到您!

1
绑定和数据库之间绝对没有关联。不要听从@blackbishop的建议,将模型的所有字段都更改为字符串。如果存在不同类型,则这是一个非常糟糕的想法... 此外,Ebean(假设您正在使用它)或JPA根据Java属性类型生成数据库列类型。为什么要在varchar列中存储0或1?遵循以下规则:1. 对于模型使用单数名称(User而不是Users);2. 总是使用驼峰命名法进行属性命名,不要使用下划线(firstName而不是first_name,lastName而不是last_name...);3. 在绑定后获取值之前检查错误。这应该会给你这个:
public static Result createUser() {
    Form<User> form = Form.form(User.class).bindFromRequest();
    if (form.hasErrors()) {
        // Do what you have to do (i.e. : redirect to the form with a flash message)
    }
    User u = form.get();
    u.save();
    return redirect(routes.Backend.allUsers());
}

顺便提一下,在你的测试代码中,user_id被正确生成了,因为你没有设置任何验证规则,这些数据来自数据库而不是表单。


我一直使用这些规则,但是对于这个项目,如果我将我的模型命名为“User”,那么数据库名称将会是“user”,这是一个保留的SQL名称...无论如何,我会尝试使用驼峰命名法并回报结果。谢谢! - Danish Ashfaq
我尝试使用驼峰命名法...但没有成功。我真的不知道发生了什么,我仍然得到相同的结果 :/ - Danish Ashfaq
真正困扰我的是,如果我打印表单的内容(form.toString()),我会得到这个:Form(of=class models.system.Users, data={firstName=Bob, lastName=Smith, pin=567, isGuest=on, email=bob@smith.com, status=active}, value=Some(models.system.Users@5d482b), errors={}),这清楚地显示了form.get()方法对我不起作用... - Danish Ashfaq
由于 user 是 MySQL 中的保留字,只需在您的模型类中使用 @Table(name = "users")。提交表单后会插入什么数据到数据库中? - c4k
是的,我会使用@Table注释,这是更好的解决方案。在我提交表单后,“null”被插入。我可能有一个线索:检查我的编辑! - Danish Ashfaq

0

我知道这篇帖子已经有一个被接受的答案,但我想分享一下我在这个问题上的经验。

我遇到了同样的问题,并按照上面回答中提到的步骤进行了操作,即通过Eclipse自动构建,然后通过激活器进行清理和编译。然而,这并没有起作用。我尝试了很多不同的方法,甚至从头开始创建一个项目,没有创建任何Eclipse依赖项。但是,仍然没有效果。

然后,我查看了我的模型,并决定试试改变属性名称的大小写,然后哇!它奏效了!

以下是在之前我的模型的样子:

public class Test {
 public String FirstName;
 public String LastName;}

现在,我把它改成了这样:

public class Test {
  public String firstName;
  public String lastName;}

只是想指出这一点,因为它并不明显,而我来自.Net


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接