业务逻辑代码通常位于模型(model)层。客户端(比如浏览器)无法直接调用其中的代码,所以模型对象提供的功能,必须作为资源以URI方式暴露给外部。
客户端使用HTTP协议来操作这些资源,从而调用了内部的业务逻辑。但是,这种从资源到模型之间的映射是单向的:我们可以根据需要提供不同粒度的资源,可以虚拟出一些资源,还可以给某些资源起别名...
Controller层就是专门做这件事的:在模型层与传输层之间搭起一座桥梁。它使用与模型层同一种语言,以便访问和修改模型对象,但同时它又跟HTTP接口一样,是面向请求(Request)和响应(Response)的。
Controller层减少了HTTP与模型层之间的“阻抗不匹配”。
注意
不同的模型方案使用了不同的策略。有一些可以让我们直接访问模型对象,比如EJB或者Corba协议,它们使用RPC(远程过程调用)。这种交互方式与web很难兼容。
另一些技术如SOAP,尝试通过Web来访问模型层,但它也是一种PRC风格的协议,只是以HTTP为传输协议。它也不是一种程序协议。
Web的理念在根本上与面向对象不同,所以我们需要一个层来协调。
Controller综述
一个Controller就是一个位于 controllers 包中的类,其继承于 play.mvc.Controller :
示例:
package controllers;
import models.Client;
import play.mvc.Controller;
public class Clients extends Controller {
public static void show(Long id) {
Client client = Client.findById(id);
render(client);
}
public static void delete(Long id) {
Client client = Client.findById(id);
client.delete();
}
}
Controller中每一个public static方法都被称为一个action。它的签名形如:
public static void action_name(params...);
你可以在action的方法签名中定义各种参数。Play会自动从相应的HTTP参数中,取出对应的值赋过去(并进行恰当的转换),这一点非常方便。
通常一个action不需要返回值。当我们在action中调用了一个能产生“结果”的方法后,action就退出了。在本例中, render(…) 就是一个显示一个模板的可产生结果的方法。
获取HTTP参数
HTTP请求中可包含数据,这些数据可位于:
URI路径中: 如 /clients/1541, 1541就是一个动态产生的参数.
Query String: /clients?id=1541.
request body: 如果提交了一个HTML表彰,将request body中将包含以 x-www-urlform-encoded 方式转换过的数据。
对于这些情况,Play都可以取得数据,并生成一个 Map 。Key是参数名,来源于:
在conf/routes文件中定义的规则中的动态参数名
Query String中的name=value
通过表单提交的数据的name.
使用参数map
在所有的Controller类中都可以直接使用 params 这个对象。它是在 play.mvc.Controller 中定义的。这个对象中,包含了当前请求的所有数据。
示例:
public static void show() {
String id = params.get("id");
String[] names = params.getAll("names");
}
你还可以转换参数值的类型:
public static void show() {
Long id = params.get("id", Long.class);
}
其实,还有更好的办法来转换 :)
直接利用action方法的参数类型定义来转换
我们可以直接从action方法的参数定义中,直接拿到对应的参数值。方法中的参数名必须跟http传过来的参数名相同。
比如,对于如下的URL:
/clients?id=1451
我们可以定义一个包含 id 参数的action:
public static void show(String id) {
System.out.println(id);
}
我们还可以使用其它的类型,play会自动进行转换:
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 id) {
for(String anId : id) {
System.out.println(anid);
}
}
这一功能非常方便,其它的框架都很少提供(有一些提供了,但要求每个参数前都要加一个annotation,不太方便)。
内部原理是,Play会对每个action进行扫描,得到其参数信息(包括类型与参数名)。能过反射很容易得到参数类型,但得不到参数名,因为javac在编译java代码时,会忽略参数名信息。而Play内置了eclipse的编译器,并在编译时打开相关选项以记录参数名信息,所以才能成功取到。
异常
如果action方法中定义的某个参数,在http请求中找不到对应的数据,则将会把它设为默认值(对象类型设为null,基础数字类型设为0,boolean类型设为false)。如果找到了,但是其值无法转换为指定的Java类型,则会将这一错误记录在validation对象中,并使用默认值代替。
HTTP to Java 高级绑定
简单类型
Java中所有基础和常用类型,都可以自动绑定:
int, long, boolean, char, byte, float, double, Integer, Long, Boolean, Char, String, Byte, Float,Double.
注意如果某个参数HTTP请求中没有提供,或者自动转换失败,则Object类型会被设为null,而基础类型会被设为它们的默认值。
Date
如果一个日期格式如下,则它可以自动转换并绑定:
yyyy-MM-dd’T’hh:mm:ss’Z'