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

来源:互联网 发布:禁止网络标语 编辑:程序博客网 时间:2024/06/08 05:13
摘要: 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中所有基础和常用类型,都可以自动绑定:

intlongbooleancharbytefloatdoubleIntegerLongBooleanCharStringByteFloatDouble.

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会话对象具有不同的语义。您不能假定这些对象将始终位于缓存中。所以它强制你处理缓存未命中的情况,并保持你的应用程序完全无状态。

阅读全文
1 0
原创粉丝点击