controllers - niubods/playframework-notes GitHub Wiki
控制器用以粘合HTTP和模型层,一方面它使用和模型层相同的语言,方便访问和修改模型对象,同时它又和HTTP接口一样,是面向过程和请求/响应的。
一个控制器就是一个java类,放在controllers
包或它的子包中,并且是play.mvc.Controller
的子类。
控制器中的每一个公共静态方法称为一个动作。
可以在动作方法中定义参数,这些参数会由框架自动从对应的HTTP请求参数中获取。
通常一个动作方法没有return语句,方法会在调用结果方法后结束,例如render(...)
就是一个结果方法,它会执行和显示一个模板。
一个HTTP请求可以携带数据,数据可以通过下面几种形式传入:
- 作为URI的一部分:
/clients/1541
中的1541 - 作为查询字符串:
/clients?id=1541
- 作为请求体:通过HTTP表单发送
Play从以上所有形式中提取数据并存入一个Map<String, String[]>
对象中。key为参数名。
有几种方法可以在控制器中获取这些请求参数。
params对象从任何控制器类中都可以使用,这个对象包含了从当前请求中能找到的所有请求参数。
例如:
String id = params.get("id");
或者让Play为你做类型转换:
Long id = params.get("id", Long.class);
不过下面还有更好的方法
你可以直接从动作方法的签名中获取HTTP请求参数。Java参数的名字和HTTP参数的名字要一致。
例如在这个请求中:
/clients?id=1451
可以通过在动作方法的签名中定义`id`参数来获取`id`请求参数。
public static void show(String id) {
System.out.println(id);
}
也可以使用String之外的其他类型,Play框架会将请求参数转换成正确的Java类型。
public static void show(Long id) {
System.out.println(id);
}
如果请求参数是多值的,你可以声明一个数组参数:
public static void show(Long[] id) {
for(String anId : id) {
System.out.println(anid);
}
}
或者甚至是一个容器类型:
public static void show(List<Long> id) {
for(String anId : id) {
System.out.println(anid);
}
}
异常
当动作方法中参数的对应的HTTP请求参数没找到时,动作方法中相应的参数会设置为它们的默认值(对象类型为null,基本数值类型为0)。
当对应的值找到了,但是不能正确转换成要求的Java类型时,会向表单验证错误容器中添加一条错误消息,并赋予一个默认值。
所有本地类型和常见的Java都可以被自动绑定:
int
, long
, boolean
, char
, byte
, float
, double
, Integer
, Long
, Boolean
, Char
, String
, Byte
, Float
, Double
.
注意如果参数在HTTP请求中缺失或自动转换失败,对象类型会设置为null而本地类型会设置为它们的默认值。
Play框架可以自动识别出很多种日期格式并自动转换,如果要自定义日期格式可以使用`@As`注解。
文件上传在Play中非常简单,用一个multipart/form-data
编码的post请求发送到服务器,然后使用java.io.File
类型获取一个文件对象。
创建的文件和原始文件的文件名一致,它保存在一个临时目录中,并且在请求结束的时候会删除。所以你必须将它拷贝到一个安全的地方否则文件会丢失。
Play还可以利用同样简单的的命名转换规则自动绑定任何模型类。
public static void create(Client client ) {
client.save();
show(client);
}
你可以使用HTTP到Java的绑定机制自动绑定你的JPA对象。
如果你在请求中包含id,Play会在编辑之前先到数据库加载对应的实例,然后将HTTP请求参数应用其上。这样你就可以直接保存这个对象了。
自定义绑定
一个动作方法必须生成一个HTTP响应,最简单的方法就是发出一个结果对象,一旦发出了一个结果对象,正常的执行流程会被打断,然后方法就返回了。
例如:
public static void show(Long id) {
Client client = Client.findById(id);
render(client);
System.out.println("This message will never be displayed !");
}
这里render(...)
方法发出了一个结果对象,阻止了方法继续执行。
使用renderText(...)
方法可以发出一个简单结果事件,会向底层的HTTP相应流中直接写入一些文本。
renderText(unreadMessages);
你也可以使用Java标准的格式化语法对你的文本消息进行格式化:
renderText("There are %s unread messages", unreadMessages);
可以通过调用renderJSON(...)
方法返回一个JSON字符串,参数可以传入一个你自己的JSON字符串,或者传入一个object
,Play会使用GsonBuilder
对它进行序列化。
调用renderXML(...)
方法可以返回一个XML字符串,参数可以传入一个XML字符串,或者一个org.w3c.dom.Document
对象。
要提供一个二进制内容,比如放在服务器上的一个文件,可以使用renderBinary
。
也是使用renderBinary
,要提供一个文件名作为第二个参数。
如果要生成的内容比较复杂,你应该使用模板来产生相应内容。执行一个模板需要调用render(...)
方法。
模板的名称是由Play的约定推断出来的,默认的模板是由控制器和动作的名字来决定的。
可以使用renderArgs
对象向模板作用域中添加数据。
renderArgs.put("client", client);
在模板执行的时候,会定义一个client
变量,在模板中可以这样引用:
<h1>Client ${client.name}</h1>
直接将要传入的数据作为render(...)
方法的参数:
render(client);
如果你想跨多个请求保存数据,你可以把它们保存到Session或Flash作用域中。保存在Session中的数据在整个会话中都是可用的,保存在Flash中的数据只能在下次请求时可用。