您的位置:首页 > 其它

play1.x 第三弹 :控制器层(Controllers)

2017-08-07 00:00 78 查看
摘要: Play的业务逻辑代码通常位于模型(model)层。Controller层就是在模型层与传输层之间搭起一座桥梁。

谨以此系列文章进行学习、记录、分享。

注:参考了play官方文档 https://www.playframework.com/documentation/1.2.x/home
controller其实就是一个调度员,在一个请求过来的时候,来协调多个资源(对象),它使用与模型层同一种语言,以便访问和修改模型对象,但同时它又跟HTTP接口一样,是面向请求(Request)和响应(Response)的。

controller主要做的事情就是:接受请求参数,处理请求,响应请求。

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...);


参数的接收

1.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);
}


2.直接利用action方法的参数类型定义来转换

我们可以直接从action方法的参数定义中,直接拿到对应的参数值。方法中的参数名必须跟http传过来的参数名相同。

比如,对于如下的URL:

/clients?id=1451

我们可以定义一个包含
id
参数的action:

public static void show(String id) {
System.out.println(id);
}

内部原理是,Play会对每个action进行扫描,得到其参数信息(包括类型与参数名)。能过反射很容易得到参数类型,但得不到参数名,因为javac在编译java代码时,会忽略参数名信息。而Play内置了eclipse的编译器,并在编译时打开相关选项以记录参数名信息,所以才能成功取到。

HTTP to Java 高级绑定

简单类型

Java中所有基础和常用类型,都可以自动绑定:

int
,
long
,
boolean
,
char
,
byte
,
float
,
double
,
Integer
,
Long
,
Boolean
,
Char
,
String
,
Byte
,
Float
,
Double
.

Date

如果一个日期格式如下,则它可以自动转换并绑定:

yyyy-MM-dd’T’hh:mm:ss’Z' // ISO8601 + timezone

yyyy-MM-dd’T’hh:mm:ss" // ISO8601

yyyy-MM-dd

yyyyMMdd’T’hhmmss

yyyyMMddhhmmss

dd'/‘MM’/'yyyy

dd-MM-yyyy

ddMMyyyy

MMddyy

MM-dd-yy

MM'/‘dd’/'yy

使用
@As
注解,我们可以指定日期格式。

例如:

archives?from=21/12/1980

public static void articlesSince(@As("dd/MM/yyyy") Date from) {
List<Article> articles = Article.findBy("date >= ?", from);
render(articles);
}

如果没有使用
@As
注解,则Play将使用你的区域对应的默认日期格式。

文件(File)

在Play中处理文件上传非常简单。使用经
multipart/form-data
编码的请求将文件post到服务器端,同时使用
java.io.File
来获取文件:

public static void create(String comment, File attachment) {
String s3Key = S3.post(attachment);
Document doc = new Document(comment, s3Key);
doc.save();
show(doc.id);
}

Play将先取得上传的文件,保存在临时目录下,并使用上传的文件名。当这个request结束后,该文件将被删除,所以我们必须将它拷贝到一个安全的目录中,否则就找不到了。

Map<String, String>

public static void show(Map<String, String> client) {
…
}

一个如下的query string:

?client.name=John&client.phone=111-1111&client.phone=222-2222

将会把变量client绑定到一个含有两个元素的map上。第一个元素的key是
name
,值是
John
,第二个key是
phone
值是
111-1111, 222-2222
.

POJO对象绑定

Play还可以使用简单的命名约定将参数绑定到一个pojo对象上。

public static void create(Client client ) {
client.save();
show(client);
}

可以使用像下面这样的query string,来调用该action以创建一个client对象:

?client.name=Zenexity&client.email=contact@zenexity.fr


JPA对象绑定

我们可以将一个JPA对象与HTTP自动绑定起来。

我们可以在HTTP参数中提供
user.id
字段。当Play发现这个字段时,它会先到数据库中取出相应的实例,然后把HTTP请求中其它的参数赋过去。所以我们可以直接save它。

public static void save(User user) {
user.save(); // ok with 1.0.1
}

我们可以使用同样的方式来更新完整的对象图,但是必须对每一个子对象提供ID

结果的返回

返回模板:

render(…) 自动以控制器和操作名称解析默认模板路径

将数据添加到模板范围(域)1.普通方法

通常模板需要数据。可以使用renderArgs对象将这些数据添加到模板范围:

public class Clients extends Controller {

public static void show(Long id) {
Client client = Client.findById(id);
renderArgs.put("client", client);
render();
}
}


将数据添加到模板范围(域)2.简单方法

可以使用render(…)方法参数将数据直接传递到模板,这种情况下页面访问的变量名和java变量具有相同的名称。只能以这种方式传递局部变量

指定另一个模板

如果你不想使用默认模板,可以通过在
renderTemplate(…)
中的第一个参数中指定另一个模板名称。例如:

renderTemplate("Clients/showClient.html", id, client);


重定向

redirect(…)
方法会产生一个重定向事件,接着会产生一个HTTP Redirect响应。

返回一些文本内容

renderText(…)

返回一个JSON字符串

renderJSON(…)

返回XML字符串

renderXml(…)

方法返回内容类型设置为的XML字符串
text/xml
。在这里,您可以指定自己的XML字符串,传递
org.w3c.dom.Document
对象,或传递将由XStream序列化程序序列化的POJO。

返回二进制内容

renderBinary(…) 例如返回图片

下载文件作为附件

您可以设置HTTP标头来指示Web浏览器将二进制响应视为“附件”,通常会导致Web浏览器将文件下载到用户的计算机。为此,将文件名作为参数传递给renderBinary方法,这将使Play设置Content-Disposition响应头,提供文件名。例如,假设User上一个示例中的模型作为一个photoFileName属性:

renderBinary(binaryData, user.photoFileName);

拦截器(Interceptions)

一个controller可以定义多个拦截器方法。拦截器作用于一个controller及其所有子类的所有action方法上。对于定义一些所有action共用的操作时,使用拦截器非常有用,比如:检查用户是否已经登录(有没有访问权),截入request范围内的数据,等等。

这些方法必须是
static
但不一定是
public
。您必须使用有效的拦截标记来注释这些方法。

@Before

如果方法上有
@Before
注解,则它将在该controller中的每一个action被调用前被执行。

@After

使用
@After
注释注释的方法将在此Controller的每个操作调用后执行。

@Catch

@Catch
如果另一个动作方法抛出指定的异常,则会调用注释的方法。抛出的异常作为参数传递给@Catch方法。(就是其他方法抛异常了我来处理)

@Finally

使用
@Finally
注释的方法总是在对此Controller执行每个操作后执行。

@With注释添加更多拦截器

因为Java不允许多重继承,所以依靠Controller层次结构应用拦截器可能非常有限。但是您可以在完全不同的类中定义一些拦截器,并将它们与使用@With注释的任何控制器进行链接。

会话和Flash范围

如果您必须在多个HTTP请求之间保留数据,则可以将其保存在会话或Flash范围中。存储在会话中的数据在整个用户会话期间可用,并且存储在闪存范围内的数据仅可用于下一个请求。

了解Session和Flash数据不会存储在服务器中,而是使用Cookie机制添加到每个后续HTTP请求中非常重要。因此,数据大小非常有限(最多4 KB),您只能存储String值。

当然,cookie是用密钥签名的,所以客户端不能修改cookie数据(否则会被无效)。播放会话不旨在用作缓存。如果需要缓存与特定会话相关的某些数据,则可以使用Play内置缓存机制,并使用session.getId()键来保持与特定用户会话相关。

例:

public static void index() {
List messages = Cache.get(session.getId() + "-messages", List.class);
if(messages == null) {
// Cache miss
messages = Message.findByUser(session.get("user"));
Cache.set(session.getId() + "-messages", messages, "30mn");
}
render(messages);
}

关闭Web浏览器时,会话将过期,除非您配置application.session.maxAge

缓存与传统的Servlet HTTP会话对象具有不同的语义。您不能假定这些对象将始终位于缓存中。所以它强制你处理缓存未命中的情况,并保持你的应用程序完全无状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Play Framework