play框架05--控制层

5.1、概述  

Play的控制层位于应用的controllers包中,其中的Java类即为控制器(Controller)。如图4.1所示,Application.java和MyController.java都属于控制层。

图片资源/controller.png

(图4.1 控制器为controllers包中的Java类)

      控制器需要继承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();
   
}
 
}

      在控制器中,每个以public static声明,返回值为void的方法称为Action。Action的方法声明如下:

public static void action_name(params…);

      Play会自动将HTTP请求参数转化为与之相匹配的Action方法参数,这部分内容会在后面的获取HTTP参数小节进行详细讲解。通常情况下,Action方法无需返回任何值,以调用结果方法来终止执行。在上述例子中,render(…)方法就是用来渲染模板的结果方法。

    HTTP请求中往往包含各种参数,这些参数的传递形式如下:

  • URI路径:在路径/clients/1541中,1541是URI的动态部分。
  • 查询字符串:clients?id=1541。
  • 请求体:如果请求是来自HTML的表单提交(GET或者POST),那么请求体包含的是表单数据(采用x-www-urlform-encoded作为编码格式)。


      针对以上几种情况,Play会自动提取这些HTTP参数并将他们保存在Map<String,String>类型的变量中,以参数名作为Map的key。这些参数名分别来自于:

  • URI中动态部分的名称(在routes文件中定义)。
  • 查询字符串中“名称/值”对中的名称部分 。
  • 采用x-www-urlform-encoded编码的表单数据的参数名。

5.2获取HTTP参数

5.2.1 使用Map参数#

      HTTP请求中参数对象(params)在任何控制器中都是可访问的(该实现在play.mvc.Controller超类中定义),它包含了当前所有HTTP请求的参数,并且可以通过get()方法得到,具体如下:

public static void show(){
   
String id=params.get("id");
   
String[] names=params.getAll("name");
}

      这些参数也可以进行类型转换:

public static void show(){
   
Long id=params.get("id",Long.class);
}

      本节将推荐一种更好的解决方案。Play框架提供了自动将Action声明的参数与HTTP参数自动匹配的功能(只需要保持Action方法的参数名和HTTP参数名一致即可):

/clients?id=1541 

      Action方法可以在声明中以id作为参数,以此匹配HTTP中变量名为id的参数:

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

      当然,也可以使用其他Java参数类型,而不仅仅是String。在下面的例子中框架会自动将参数转换为正确的数据类型:

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

      如果参数含有多个值,那么可以定义数组参数,具体如下:

public static void show(Long[] id){
   
for(Long anId:id){
       
System.out.println(anId);
   
}
}

      参数甚至可以是List类型:

public static void show(List<Long> id){
   
for(Long anId:id){
       
System.out.println(anId);
   
}
}


注意:

如果Action与HTTP之间的参数无法匹配,Play会将该参数设置为默认值(通常情况下对象类型为null,原始数据类型为0)。如果参数可以匹配但不能正确进行数据转换,那么Play会先生成错误并添加到验证器的error对象集合中,然后将参数设置为默认值。


4.2.2 高级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


4.2.3 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


4.2.4 自定义绑定#

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


@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类定义绑定的例子:

@Global
public 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])
       
);
   
}
}

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

转载http://www.playframework.cn/wiki/PlayFramework%20Tutorial

猜你喜欢

转载自blog.csdn.net/J_M_S_H_T/article/details/80833343
今日推荐