1 高级HTTP绑定#

简单类型

Play可以实现所有Java原生的简单数据类型的自动转换,主要包括:int,long,boolean,char,byte,float,double,Integer,Long,Boolean,Char,String,Float,Double。

日期类型

如果HTTP参数字符串符合以下几种数据格式,框架能够自动将其转换为日期类型:

  • 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);}

也可以根据不同地区的语言习惯对日期的格式做进一步的优化,具体如下:

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

在这个例子中,对于法语和德语的日期格式是dd-MM-yyyy,其他语言的日期格式是MM-dd-yyyy。语言值可以通过逗号隔开,且需要与参数的个数相匹配。

如果没有使用@As注解来指定,Play会采用框架默认的日期格式。为了使默认的日期格式能够正常工作,按照以下方式编辑application.conf文件:

date.format=yyyy-MM-dd

在application.conf文件中设置默认的日期格式之后,就可以通过${date.format()}方法对模板中的日期进行格式化操作了。

日历类型

日历类型和日期类型非常相像,当然Play会根据本地化选择默认的日历类型。读者也可以通过@Bind注解来使用自定义的日历类型。

文件类型

在Play中处理文件上传是件非常容易的事情,首先通过multipart/form-data编码的请求将文件发送到服务器,然后使用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);}

新创建文件的名称与原始文件一致,保存在应用的临时文件下(Application_name/tmp)。在实际开发中,需要将其拷贝到安全的目录,否则在请求结束后会丢失。

数组和集合类型

所有Java支持的数据类型都可以通过数组或者集合的形式来获取。数组形式:

public static void show(Long[] id){        ...}

List形式:

public staic void show(List<Long> id){        ...}

集合形式:

public static void show(Set<Long> id){        ...}

Play还可以处理Map<String, String>映射形式:

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

例如下面的查询字符串会转化为带有两个元素的map类型,第一个元素key值为name,value为John;第二个元素key值为phone,value为111-1111, 222-2222。:

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

POJO对象绑定

Play使用同名约束规则(即HTTP参数名必须与模型类中的属性名一致),自动绑定模型类:

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

以下的查询字符串可以通过上例的Action创建client:

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

框架通过Action创建Client的实例,并将HTTP参数解析为该实例的属性。如果出现参数无法解析或者类型不匹配的情况,会自动忽略。

参数绑定是递归执行的,这意味着可以深入到关联对象:

?client.name=Zenexity&client.address.street=64+rue+taitbout&client.address.zip=75009&client.address.country=France

Play的参数绑定提供数组的支持,可以将对象id作为映射规则,更新一组模型对象。假设Client模型有一组声明为List<Customer>的customers属性,那么更新该属性需要使用如下查询字符串:

?client.customers[0].id=123&client.customers[1].id=456&client.customers[2].id=789

2 JPA对象绑定#

通过HTTP参数还可以实现JPA对象的自动绑定。Play会识别HTTP请求中提供的参数user.id,自动与数据库中User实例的id进行匹配。一旦匹配成功,HTTP请求中的其他User属性参数可以直接更新到数据库相应的User记录中:

public static void save(User user){    user.save();}

和POJO映射类似,可以使用JPA绑定来更改对象,但需要注意的是必须为每个需要更改的对象提供id:

user.id = 1&user.name=morten&user.address.id=34&user.address.street=MyStreet 

3 自定义绑定#

绑定机制支持自定义功能,可以按照读者的需求,自定义参数绑定的规则。

@play.data.binding.As

@play.data.binding.As注解可以依据配置提供绑定的支持。下例使用DateBinder指定日期的数据格式:

public static void update(@As("dd/MM/yyyy") Date updatedAt) {    ...}

@As注解还具有国际化支持,可以为每个本地化提供专门的注解:

public static void update(        @As(            lang={"fr,de","en","*"},            value={"dd/MM/yyyy","dd-MM-yyyy","MM-dd-yy"}        )        Date updatedAt    ) {    ...}

@As注解可以和所有支持它的绑定一起工作,包括用户自定义的绑定。以下是使用ListBinder的例子:

public static void update(@As(",") List<String> items) {    ...}

上例中的绑定使用逗号将字符串分隔成List。

@play.data.binding.NoBinding

@play.data.binding.NoBinding注解允许对不需要绑定的属性进行标记,以此来解决潜在的安全问题。比如:

//User为Model类public class User extends Model {    @NoBinding("profile") public boolean isAdmin;    @As("dd, MM yyyy") Date birthDate;    public String name;}

//editProfile为Action方法public static void editProfile(@As("profile") User user) {    ...}

在上述例子中,user对象的isAdmin属性始终不会被editProfile方法(Action)所修改,即使有恶意用户伪造POST表单提交user.isAdmin=true信息,也不能修改user的isAdmin权限。

play.data.binding.TypeBinder

@As注解还提供完全自定义绑定的功能。自定义绑定必须是TypeBinder类的实现:

public class MyCustomStringBinder implements TypeBinder<String> {

    public Object bind(String name, Annotation[] anns, String value,     Class clazz) {        return "!!" + value + "!!";    }}

定义完成后,就可以在任何Action中使用它:

public static void anyAction(@As(binder=MyCustomStringBinder.class) String name) {        ...}

@play.data.binding.Global

Play中还可以自定义全局Global绑定。以下是为java.awt.Point类定义绑定的例子:

@Globalpublic class PointBinder implements TypeBinder<Point> {

    public Object bind(String name, Annotation[] anns, String value,     Class class) {        String[] values = value.split(",");        return new Point(            Integer.parseInt(values[0]),            Integer.parseInt(values[1])        );    }}

因此外部模块很容易通过自定义绑定来提供可重用的类型转换组件。

3、结果返回

Action方法需要对客户端作出HTTP响应,最简单的方法就是发送结果对象。当对象发送后,常规的执行流程就会中断。以下面这段代码为例,最后一句System.out.println的输出不会被执行:

public static void show(Long id) {    Client client = Client.findById(id);    render(client);    System.out.println("This message will never be displayed !");}

render(…)方法向模板发送client对象,之后的其他语句将不会执行,所以在控制台中,并不会打印出“This message will never be displayed !”。

3.1 返回文本内容#

renderText(…)方法直接将文本内容写到底层HTTP响应中:

public static void countUnreadMessages(){    Integer unreadMessages=MessagesBos.countUnreadMessage();    renderText(unreadMessages);}

也可以通过Java标准的格式化语法对输出的文本进行处理:

public static void countUnreadMessages(){    Integer unreadMessages=MessagesBox.countUnreadMessages();    renderText("There are %s unread messages",unreadMessages);}

3.2 返回JSON字符串#

越来越多的应用使用JSON作为数据格式进行交互,Play对此进行了很好的封装,只需要使用renderJSON(…)方法就可以轻松地返回JSON字符串。在使用renderJSON(…)方法时,Play会自动将服务器返回的响应的content type值设置为application/json,并且将renderJSON(...)方法中的参数以JSON格式返回。

在使用renderJSON(...)方法时,可以输入字符串格式的参数,自行指定JSON返回的内容。

public static void countUnreadMessages() {    Integer unreadMessages = MessagesBox.countUnreadMessages();    renderJSON("{\"messages\": " + unreadMessages +"}");}

以上范例在使用renderJSON(...)方法时,传入了拼接成JSON格式的字符串参数。Play框架会对其进行自动设置,改变content type的值为application/json。

当然,renderJSON(...)方法的功能并不只有这些。因为大部分的应用需求,都会要求服务端返回比较复杂的JSON格式,如果都采用字符串拼接的方式组成JSON内容,就太不人性化了。renderJSON(...)的输入参数还可以是复杂的对象,如果采用这种方式使用renderJSON(...)方法,Play在执行renderJSON(...)时,底层会先调用GsonBuilder将对象参数进行序列化,之后再将复杂的对象以JSON的格式返回给请求。这样开发者就可以完全透明地使用renderJSON(...)方法,不需要做其他的任何操作了,以下代码范例将会展示renderJSON(...)的这个功能。

public static void getUnreadMessages() {    List<Message> unreadMessages = MessagesBox.unreadMessages();    renderJSON(unreadMessages);}

3.3 返回XML字符串#

与使用renderJSON(...)方法返回JSON内容类似,如果用户希望以XML格式对内容进行渲染,可以在Controller控制器中直接使用renderXml(…)方法。 使用renderXml(...)方法时,Play会自动将服务器返回的响应的content type值设置为application/xml。

在使用renderXml(...)方法时,可以输入字符串格式的参数,自行指定XML返回的内容。

public static void countUnreadMessages() {    Integer unreadMessages = MessagesBox.countUnreadMessages();    renderXml("<unreadmessages>"+unreadMessages+"</unreadmessages>");}

如果希望将复杂的对象以XML格式进行渲染,可以在使用renderXml(...)方法时输入org.w3c.dom.Document格式的对象,或者直接输入POJO对象。以POJO对象作为参数使用renderXml(...)方法时,Play会使用XStream将其进行序列化操作。同样的,这些序列化操作都不需要由开发者去做,全部交给Play就行,开发者需要做的就是按照规范简单地调用renderXml(...)方法即可。

public static void getUnreadMessages() {    Document unreadMessages = MessagesBox.unreadMessagesXML();    renderXml(unreadMessages);

3.4 返回二进制内容#

Play为开发者提供了renderBinary(...)方法,可以非常方便的返回二进制数据(如存储在服务器里的文件、图片等)给客户端。以下代码范例将会展示如何使用renderBinary(...)方法进行二进制图片的渲染。

public static void userPhoto(long id) {    final User user = User.findById(id);    response.setContentTypeIfNotSet(user.photo.type());   java.io.InputStream binaryData = user.photo.get();   renderBinary(binaryData);} 

首先,开发者需要建立用于持久化的域模型User,该User模型具有play.db.jpa.Blob类型的属性photo。play.db.jpa.Blob是经过Play封装的特有的属性类型,可以很方便的处理二进制数据。之后,在Controller控制器中使用时,需要调用域模型的findById(...)方法加载持久化的数据,并将图片以二进制数据流InputStream的形式进行渲染。

3.5 下载附件功能#

如果开发者希望将存储在服务器端的文件,采用下载的形式渲染给客户端用户,需要对HTTP的header进行设置。通常的做法是通知Web浏览器将二进制响应数据以附件的形式,下载至用户的本地电脑上。在Play中完成这个功能非常简单,只需要在使用renderBinary(...)方法时多传入一个文件名的参数即可。这样做会触发renderBinary(...)的额外功能,提供文件名并设置响应头的Content-Disposition属性。之后二进制文件(包括图片)将会以附件下载的形式,渲染给用户。

public static void userPhoto(long id) {    final User user = User.findById(id);    response.setContentTypeIfNotSet(user.photo.type());   java.io.InputStream binaryData = user.photo.get();   renderBinary(binaryData, user.photoFileName); } 

3.6 执行模板#

如果需要响应的内容比较复杂,那么就应该使用模板来进行处理:

public class Clients extends Controller{    public static void index(){        render();    }}

模板的名称遵从Play的约束规则,默认的模板路径采用控制器和Action的名称相结合的方式来定义,比如在上述例子中,模板对应的路径为:app/views/Clients/index.html。

3.7 为模板作用域添加数据#

通常情况下模板文件都需要数据进行显示,可以使用renderArg()方法为模板注入数据:

public class Clients extends Controller {

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

}

在模板执行过程当中,client变量可以被使用:

<h1>Client ${client.name}</h1>

3.8 更简单方式#

这里介绍一种更简单的方式向模板传递数据。直接使用render(…)方法注入模板数据:

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

以该方式进行数据传递,模板中可访问的变量与Java本地变量的名称(也就是render()方法中的参数名)一致。当然也可以同时传递多个参数:

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

注意:

render()方法只允许传递本地变量。

3.9 指定其他模板进行渲染#

如果读者不希望使用默认的模板进行渲染,那么可以在renderTemplate(…)方法的第一个参数中指定其他自定义的模板路径,例如:

public static void show(Long id) {    Client client = Client.findById(id);    renderTemplate("Clients/showClient.html", id, client);    

3.10 重定向URL#

redirect(…)方法产生HTTP重定向响应,可以将请求转发到其他URL:

public static void index(){    redirect("http://www.oopsplay.org");}

3.11 自定义Web编码#

Play推荐开发者使用UTF-8作为应用开发的编码格式,如果不进行任何设置,Play框架默认使用的也就是UTF-8格式。但是具体情况并不总是这么理想,有些特殊的需求可能要求某些响应(response)的格式为ISO-8859-1,或者要求整个应用都必须保持ISO-8859-1编码。

为当前响应设置编码格式

如果需要改变某一个响应(response)的编码格式,可以直接在Controller控制器中进行修改,具体做法如下所示:

response.encoding = "ISO-8859-1";

当开发表单提交功能时,如果开发者希望某一表单提交的内容采用非框架默认使用的编码(即Play框架采用默认的编码格式UTF-8,而该form表单提交的内容希望采用ISO-8859-1编码格式),Play的做法有一些特殊。在书写form表单的HTML代码时,需要对采用何种编码格式进行两次标识。首先需要在<form>标签中添加accept-charset属性(如:accept-charset="ISO-8859-1"),accept-charset属性会通知浏览器当form表单提交的时候,采用何种编码格式;其次,需要在form表单中添加hidden隐藏域,name属性规定为“_charset_”,value属性为具体需要的编码格式,这样做的目的是当form提交的时候,可以通知服务端的Play采用何种编码方式,具体范例如下:

<form action="@{application.index}" method="POST" accept-charset="ISO-8859-1">    <input type="hidden" name="_charset_" value="ISO-8859-1"></form>

定义全局编码格式

通常情况下,整个应用应该保持统一的编码格式。如果开发者需要设置应用全局的编码格式,可以在application.conf配置文件中修改application.web_encoding属性,配置相应的编码。

4、Action链

lay中的Action链与Servlet API中的forward不尽相同。Play的每次HTTP请求只能调用一个Action,如果需要调用其他的Action,那么必须将浏览器重定向到相应的URL。在这种情况下,浏览器的URL始终与正在执行的Action保持对应关系,使得后退、前进、刷新操作更加清晰。

调用控制器中其他Action方法也可以实现重定向,框架会拦截该调用并生成正确的HTTP重定向。具体实现如下:

public class Clients extends Controller {

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

    public static void create(String name) {        Client client = new Client(name);        client.save();        show(client.id);    }}

相应的路由规则定义如下:

GET            /clients/{id}                              Clients.showPOST           /clients                                   Clients.create

按照定义,Action链的生命周期为:

  • 浏览器向/clients发送POST请求;
  • 路由器调用Clients控制器中的create方法;
  • create方法直接访问show方法;
  • Java调用被拦截,路由器逆向生成带有id参数的URL来调用Clients.show;
  • HTTP响应重定向为:/clients/3132;
  • 浏览器地址栏中URL展现为:/clients/3132;

Action链

play框架使用起来(7)相关推荐

  1. ssh(Struts+spring+Hibernate)三大框架整合-简述

    ssh(Struts+spring+Hibernate)三大框架配合使用来开发项目,是目前javaee最流行的开发方式,必须掌握: 注意: 为了稳健起见,每加入一个框架,我们就需要测试一下,必须通过才 ...

  2. Gin 框架学习笔记(03)— 输出响应与渲染

    在 Gin 框架中,对 HTTP 请求可以很方便有多种不同形式的响应.比如响应为 JSON . XML 或者是 HTML 等. ​ Context 的以下方法在 Gin 框架中把内容序列化为不同类型写 ...

  3. Gin 框架学习笔记(02)— 参数自动绑定到结构体

    参数绑定模型可以将请求体自动绑定到结构体中,目前支持绑定的请求类型有 JSON .XML .YAML 和标准表单 form数据 foo=bar&boo=baz 等.换句话说,只要定义好结构体, ...

  4. QT学习之状态机框架

    状态机框架 创建状态机

  5. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

  6. 开源自动化机器学习框架

    20211101 在 Airbnb 使用机器学习预测房源的价格 https://blog.csdn.net/weixin_33735077/article/details/87976278?spm=1 ...

  7. Keras框架下的保存模型和加载模型

    在Keras框架下训练深度学习模型时,一般思路是在训练环境下训练出模型,然后拿训练好的模型(即保存模型相应信息的文件)到生产环境下去部署.在训练过程中我们可能会遇到以下情况: 需要运行很长时间的程序在 ...

  8. Adam那么棒,为什么还对SGD念念不忘 (1) —— 一个框架看懂优化算法

    机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着丹药出炉了. 不过,当过厨子的都知道,同样的食材,同样的菜谱,但火候不一样了, ...

  9. 一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    Adam那么棒,为什么还对SGD念念不忘 (1) -- 一个框架看懂优化算法 机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着 ...

  10. 大三后端暑期实习面经总结——SSM微服务框架篇

    博主现在大三在读,从三月开始找暑期实习,暑假准备去tx实习啦!总结下了很多面试真题,希望能帮助正在找工作的大家!相关参考都会标注原文链接,尊重原创! 目录 1. mvc.mvp.mvvm MVC架构 ...

最新文章

  1. 国际顶级学术会议SIGIR 2020开幕在即,重量级嘉宾带你窥探信息检索前沿
  2. 撩课-Java每天5道面试题第11天
  3. 一些今天看到的好句子
  4. ZOJ 38727(贪心)
  5. 扫地机器人粘住老鼠板怎么办_家里老鼠的危害性及如何有效灭鼠
  6. 【网址收藏】达内Django视频笔记收藏
  7. 关于CefSharp的坎坷之路
  8. uniapp的目录结构反思与整理 app.vue【base】pages.json【配置】main.json【框架入口文件】
  9. 传感器工作原理_荧光氧气传感器工作原理简介
  10. 统计学习方法读书笔记3-感知机SVM
  11. 怎么屏蔽还有照片_朋友圈该不该屏蔽父母?网友:发个自拍还被嫌丑,我能怎么办.........
  12. python 使打开的浏览器最大化
  13. 【毕业设计】深度学习人脸表情识别系统 - python
  14. 云桌面优缺点_相比传统PC,云桌面优缺点在哪里?
  15. Word长篇文档排版技巧
  16. Apache安全配置
  17. mysql全称_mysql全称
  18. errorCode 1045,state 28000: Access denied for user 'mysql'@'localhost' (using password: YES)
  19. docker学习笔记---基础入门
  20. reactive() 函数

热门文章

  1. NLP-Beginner:自然语言处理入门练习----task 1基于机器学习的文本分类
  2. C# HttpWebRequest 模拟下载
  3. 直播弹幕系统(三)- 直播在线人数统计
  4. Taylor公式和插值多项式
  5. python 苹果笔记本电脑报价_千万别花冤枉钱!大学生买本得这么选!
  6. 虚拟机与宿主机网络配置——可互通可上网
  7. 微信小程序毕业设计——社区老人互助管理平台
  8. 触屏笔哪个牌子好用?高性价比的电容笔推荐
  9. 如何注册自己的企业邮箱?
  10. java 字符串相加问题