play




play基本命令
    play new 项目名             创建新的play应用
    play eclipsify 项目名       将play应用转化为Eclipse项目
    play run                    启动当前路径下的play应用(需要在play应用的根目录下使用)(会打印输出信息,阻塞当前进程)
    play start                  启动当前路径下的play应用(需要在play应用的根目录下使用)
    play stop                   停止当前路径下的play应用(需要在play应用的根目录下使用)



创建一个play应用
    打开命令行,进入需要创建项目的路径
    play new 项目名
    What is the application name? [xxx] 回车就行
    这样就在当前目录创建了一个play应用



搭建一个play框架的eclipse项目
    打开命令行,进入需要创建项目的路径
    cmd --> play new 项目名
    What is the application name? [xxx] 回车就行
    cmd --> play eclipsify 项目名
    打开eclipse,导入项目
    问题:
        解决调试错误
            打开/eclipse/项目名称.launch
            搜索并删除 -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
        修改引用的play.jar为相对路径(团队合作)
            <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xdebug -Dplay.debug=yes -Dplay.id= -Dapplication.path=&quot;${project_loc:dosql}&quot; -Djava.endorsed.dirs=&quot;-javaagent:&quot;/lib/play-1.2.3.jar&quot;"/>
            要注意项目名称(${project_loc:dosql})和play.jar包版本



play应用目录规范
    app                 应用文件夹
        controllers         控制器所在包            
        models              模型所在包
        views               页面所在文件夹
            Application         正常页面所在文件夹
            errors              错误页面所在文件夹
    test                测试文件夹,存放测试代码         
    conf                配置文件文件夹
        application.conf    主配置文件
        routes              路由配置文件
        messages            国际化配置文件
        dependencies.yml
    lib                 依赖jar文件
    public              静态文件文件夹
        images              图片文件夹
        javascripts         js文件夹
        stylesheets         样式表文件夹
    eclipse             eclipse启动文件(eclipse应用中存在)




路由语法
    配置
        Play路由器使用的配置文件为conf/routes
        路由配置总是从HTTP方法开始,URI作为中间部分,最后的元素是Java调用。在路由文件中可以使用#进行注释:
        格式:   HTTP方法(大写)      URI表达式       要调用的方法
        如:     GET                 /clients        Application.index
    HTTP方法
        HTTP协议支持GET,POST,PUT,DELETE,HEAD方法,用于指定客户请求服务器的动作
        可使用*号匹配所有请求方式
            *       /clients        Application.index
    URI表达式
        可使用{}匹配动态内容
            /clients/{id}
            匹配 /clients/12121  或 /clients/toto
            并且匹配的部分,控制器可以在HTTP参数map中获取该值
        可使用正则 
            格式: /clients/{<正则>id}
            例: /clients/{<[0-9]+>id} 匹配 id为数字的值
        ?
            请求路径后的/的有无是有区别的,可使用?消除区别
            例: /clients/?
            注意: URI除了尾斜线不允许有其他可选的部分。
    Java调用
        控制器规则
            控制器需作为play.mvc.Controller的子类定义在controllers包中
            控制器中必须存在指定的Action方法
            必须声明为public static void方法
        一般格式
            类的全限定名称.方法名
            如果包名为controllers,则可以省略
            例: Application.index 表示将执行 controllers.Application类中的无参方法index
        404
            表示匹配的url将被忽略
        指定静态参数
            GET     /home           Application.page(id:'home')
            GET     /pages/{id}     Application.page
            则如果路径为/home,则会调用page方法,并将参数id的值设为"home"
        静态资源
            文件夹
                格式: staticDir:路径
                例  GET     /public/        staticDir:public
                当客户端请求/public/*路径时,Play会从应用的public文件夹中获取相应的静态资源。这些资源将直接响应给客户端,并不需要服务器做进一步加工处理
            文件
                格式: staticFile:路径
                例  GET     /home           staticFile:/public/html/index.html
                当客户端通过GET方法请求/home时,服务器将不做任何处理直接把/public/html目录下面的index.html文件返回给客户端。
    变量和脚本
        与模板中的使用方法类似,在routes文件中可以使用${...}作为变量表达式,使用%{...}作为脚本表达式
        例
            %{ context = play.configuration.getProperty('context', '') }%
            GET    ${context}         Secure.login
    优先级
        从上往下进行匹配,将执行最先匹配到的方法



控制层
    概述
        Play的控制层位于/app/controllers包中
        控制器需要继承play.mvc.Controller
        控制器中,每个以public static声明,返回值为void的方法称为Action
        Play会自动将HTTP请求参数转化为与之相匹配的Action方法参数(路由)
        Action以调用结果方法来终止执行,即render系列的方法
    获取HTTP参数
        保存参数
            play会自动将请求中的参数保存起来
            包括 路由中的动态部分,查询字符串 和 请求体中的参数和值
        使用params字段获取
            play.mvc.Controller超类中定义了params字段,可通过get和getAll方法获得请求中的值
                public String get(String key)
                public String get(String key,Class<?> clazz) //可进行类型转换
                public String[] getAll(String key)
        在Action中定义同名参数获取
            play自动在调用Action时将值放入到同名参数中,
        注意
            如果Action与HTTP之间的参数无法匹配,Play会将该参数设置为默认值(通常情况下对象类型为null,原始数据类型为0)。
    参数类型转换
        简单类型
            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注解,指定特定格式的日期,例如
                public static void articlesSince(@As("dd/MM/yyyy") Date from) 
        文件类型(文件上传)
            只需在Action方法添加一个File类型的参数,参数名称要和页面提交的表单名称相同即可
            前台
                <form action="/Application/save" method="post" enctype="multipart/form-data">
                    <input name="imageFile" type="file"><br>
                    <input type="submit" value="提交">
                </form>
            后台
                public static void save(File imageFile) {
                    File file = new File("lib/"+imageFile.getName());
                    imageFile.renameTo(file);
                }
        数组和集合类型
            需要注意的是Map类型,如果一个name提交了多个值,则多个值用逗号分隔
        POJO对象
            前台表单名称 参数名.属性名
            则自动填充到指定参数的属性中
            可以指定多层
        注意
            如果参数可以匹配但不能正确进行数据转换,那么Play会先生成错误并添加到验证器的error对象集合中,然后将参数设置为默认值。
    结果返回
        返回文本内容 
            renderText(CharSequence pattern, Object... args)
                还可以通过Java标准的格式化语法对输出的文本进行处理
                renderText("There are %s","张三")
        返回JSON
            renderJSON(Object o)
        返回xml
            renderXml(Object o)
        返回二进制内容
            不是作为附件
                renderBinary(InputStream is)
            作为附件
                renderBinary(InputStream is,String fileName)
        返回模板
            render(Object... args)
                如果一个参数的没有,则返回 app/views/当前控制器类名/当前action名称.html
                第一个参数如果是字符串并且是局部变量(不是没有引用),则为模板地址,否则为传递的数据
                后面的参数为传递的数据,只允许传递局部变量,否则被忽略
                模板的地址为相对路径, 例 Application/index.html 表示 /app/viewx/下的Application/index.html
            renderTemplate(String templateName, Object... args)
            renderTemplate(String templateName, Map<String,Object> args)
            renderTemplate(Map<String,Object> args) //使用默认模板
        注意
            当调用结果方法后,后面的代码不会被执行到
    为模板传递数据
        可以使用renderArg字段
            renderArgs.put("client", "张三");
            在页面获取 ${client}
        可以追加到render的参数中
            render();
            传递的数据必须是局部变量,否则被忽略,模板中可访问的变量与Java局部变量的名称一致
    重定向
        redirect(String url)
    设置编码(默认utf-8)
        为当前响应设置编码
            response.encoding = "ISO-8859-1"
        为当前请求设置编码
            1. 在form标签中添加属性 accept-charset="ISO-8859-1" 通知浏览器当form表单提交的时候采用何种编码格式
            2. 在form标签中添加表单元素(隐藏域) name为"_charset_" 通知服务端的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属性
    Action链
        Play的每次HTTP请求只能调用一个Action,如果需要调用其他的Action,那么必须将浏览器重定向到相应的URL
        调用控制器中其他Action方法也可以实现重定向,框架会拦截该调用并生成正确的HTTP重定向。
    拦截器
        概述
            控制器中可以定义拦截方法(也可称之为拦截器),为控制器及其子类的所有Action提供服务
            拦截器方法不能定义为public,但必须是static,并通过有效的拦截标记进行注解
        @Before
            使用@Before注解的方法会在每个Action调用之前执行
            属性
                unless 使用unless参数列出需要排除的方法
                only 使用only参数把需要拦截的方法列举出来
                unless和only参数对@After,@Before以及@Finally注解都适用
            例:
                @Before(unless="login")
                static void checkAuthentification() {
                    if(session.get("user") == null) login();
                }
        @After
            使用@After注解的方法会在每个Action调用之后执行
        @Catch
            如果有Action方法抛出了异常,那么使用@Catch注解的方法就会执行
            抛出的异常会以参数的形式传递到@Catch注解的方法中
            例:
                @Catch(IllegalStateException.class)
                public static void logIllegalState(Exception e) {
                    System.out.print(e);
                }
            属性
                value       拦截的异常类型的Class对象
                priority    多个@Catch指定优先级
        @Finally
            @Finally注解的方法总是在每个Action调用之后执行(无论Action是否成功执行)
        @with
            使用@with注解调用其他控制器中已经定义好的拦截方法
            在类声明上使用,相当于本类定义了那么多拦截方法
    Session和Flash作用域
        在Play开发中,如果需要在HTTP请求之间保存数据,可以将数据保存在Session或者Flash内。
        保存在Session中的数据在整个用户会话中都是有效的,而保存在Flash的数据只对下一次请求有效。
        Session和Flash作用域中的数据都是采用Cookie机制添加到随后的HTTP响应中的(并没有存储在服务器上的),所以数据大小非常有限(不能超过4K),而且只能存储字符串类型的数据。
        如果需要在特定的会话中缓存一些数据,那么可以使用Play内置的缓存机制



模板
    模板语法
        概述
            表达式      ${…}
            标签        #{tagName /}
            引用        @{…} 或者 @@{…}
            国际化      &{…}
            注释        *{...}*
            脚本        %{...}%
        表达式 ${…}
            例: ${client.name}
            首先需要向模板注入对象client,之后就可以在模板中输出client对象的name属性
            如果不能确定向模板注入的client对象是否为null,可以使用如下Groovy快捷语法:
                <h1>Client ${client?.name}</h1>
            此时,只有client不为null的情况下,才进行client.name的输出。
        标签 #{tagName /}
            概述
                标签是能够附带参数调用的模板片段
                如果标签只有一个参数,按照约定,参数的名称为arg,并且该参数名是可以省略的
                例如,可以使用#{script}标签加载JavaScript文件
                    #{script 'jquery.js' /}
                Play模板中的标签必须是闭合的
                    #{script 'jquery.js'/}  或
                    #{script 'jquery.js'}#{/script}
            内置标签
                #{script}       引入外部js文件
                    参数
                        arg         要引入的js文件
                    例
                        #{script 'jquery.js' /}
                #{list}         迭代集合
                    参数
                        items       要遍历的集合
                        as          变量的名称
                    例
                        #{list items:client.accounts, as:'account' }
                            <li>${account}</li>
                        #{/list}
        引用 @{…}或者@@{…}
            在模板中使用@{…}引用 可以通过路由器逆向生成URL
                <a href="@{Clients.showAccounts(client.id)}">All accounts</a>
                调用了Clients控制器中的showAccounts Action方法,并传递了client.id参数
            @@{…}引用的使用语法与@{…}相同,只不过生成的是绝对URL(尤其适用于邮箱)
        国际化 &{…}
            首先需要在conf/messages文件中进行国际化定义
                clientName=The client name is %s
            之后在模板中就可以通过&{...}显示该国际化信息了
                <h1>&{'clientName',client.name}</h1>
        注释 *{…}*
            使用*{…}*标记的内容会被模板引擎忽略
        脚本 %{…}%
            脚本是更加复杂的表达式集合,能够声明一些变量或者定义一些语句
            也可以直接使用out内置对象输出动态内容
                %{
                    fullName = client.name.toUpperCase()+' '+client.forname;
                    out.print('<h1>'+fullName+'</h1>');
                }%
                <h1>Client ${fullName}</h1>
        转义
            模板引擎默认对所有的动态表达式进行转义
                如果模板中变量${title}的内容为<h1>Title</h1>,在页面输出时会自动进行转义
                ${title} --> &lt;h1&gt;Title&lt;/h1&gt;
            可以通过调用扩展方法raw(),以非转义的形式在页面中输出
                ${title.raw()} --> <h1>Title</h1>
            如果需要显示大量的非转义HTML内容,可以使用#{verbatim /}标签
                #{verbatim}
                    ${title} --> <h1>Title</h1>
                #{/verbatim}
    模板继承
        #{extends /} 和 #{doLayout /}标签进行继承
        #{get} 和 #{set}标签在模板与父模板之间进行参数传递
        例 
            父模板simpledesign.html
                <html>
                    <head>
                        <title>#{get 'title' /}</title>
                        <link rel="stylesheet" type="text/css" href="@{'/public/stylesheets/main.css'}" />
                    </head>
                    <body>
                        <h1>#{get 'title' /}</h1>
                        #{doLayout /}
                        <div class="footer">Built with the play! framework</div>
                    </body>
                </html>
            说明
                #{get 'title' /}    表示在子模板中设置值
                #{doLayout /}       起到占位的作用,其他所有继承于此模板的页面内容都将显示在#{doLayout /}所占的页面区块
            子模板
                #{extends 'simpledesign.html' /}
                #{set title:'A decorated page' /}
                This content will be decorated.
            说明
                #{extends 'xxx' /}      继承与某个模板
                #{set title:'xxx' /}    向父模板传递值
    内置标签
        常用
            #{extends}
                子模板可以通过#{extends}标签继承已经定义好的装饰模板
                示例
                    #{extends 'main.html'/}
                    <h1>Some code</h1>
            #{doLayout /}
                表示父模板中在标签处可以插入子模板的内容
                示例
                    <div id="content">
                        #{doLayout /}
                    </div>
            #{set},#{get}
                #{set}标签用于设置可以在模板中使用的变量。
                #{get}标签的作用是获取由#{set}标签定义的值
                在Play的模板中,通过get/set机制,可以使父模板和子模板间进行通信
                示例
                    #{set email:'china@oopsplay.org'}   设置了模板变量email的值
                    #{get 'email'}                      获取email的值
                可以在#{set}标签体内定义变量的值
                示例
                    #{set 'title'}
                        Profile of ${user.login}
                    #{/set}
                可以在#{get}标签体内设置当变量不存在时的缺省值,如下例当title变量不存在时,会显示Homepage。
                示例
                    <head>
                        <title>#{get 'title'}Homepage #{/} 
                    </head>
            #{script}
                用于生成一个<script>元素,可以引入/public/javascripts目录下的JavaScript文件。无需在地址前加上默认父目录/public/javascripts。
                示例
                    #{script 'jquery-1.5.1.min.js' /}
            #{stylesheet}
                可以引入/public/stylesheets目录下的CSS文件
                示例
                    #{stylesheet 'default.css' /}
            #{if},#{ifnot}
                #{ifnot}执行时具体的作用与#{if !condition}等价
                示例
                    #{if user.countryCode == 'en' }
                        Connected user is ${user}
                    #{/if}
            #{else},#{elseif}
                #{else}标签通常与#{if}标签配合使用
                示例
                    #{if user}
                        Connected user is ${user}
                    #{/if}
                    #{else}
                        Please log in
                    #{/else}
                #{else}标签也可以与#{list}标签一起使用,当使用#{list }标签进行迭代的集合为空时,可以执行#{else}标签中的内容
                示例
                    #{list items:task, as:'task'}
                        <li>${task}</li>
                    #{/list}
                    #{else}
                        Nothing to do...
                    #{/else}
            #{list}
                遍历集合
                示例
                    <ul>
                        #{list items:products, as:'product'}
                            <li>${product}</li>
                        #{/list}
                    </ul>
                    #{list items:0..10, as:'i'}
                        ${i}
                    #{/list}
                预定义变量,名称为 循环变量_XXX,下面用note代表循环变量
                    note_index:表示当前对象在集合中的序号。
                    note_isFirst:表示是否是集合中的第一个对象。
                    note_isLast:表示是否是集合中的最后一个对象。
                    note_parity:表示当前对象在集合中序号的奇偶值,可能是even或odd。
            #{include}
                在当前模板中导入另一个模板,并且当前模板中的所有变量对导入的模板透明。
                示例
                    <div id="tree">
                        #{include 'tree.html' /}
                    </div>
            #{verbatim}
                使输出内容不转义(默认转义)
                示例
                    #{verbatim}${'&amp;'}#{/verbatim}
        表单
            #{authenticityToken /}
                可以防止跨站请求伪造,同时也能消除刷新提交和后退提交所带来的困扰
                会为服务器和客户端表单生成相同的随机令牌,并以隐藏的input输入域的形式嵌入表单,一起提交到服务器
                在表单中加入即可,相当于<input type="hidden" name="authenticityToken" value="1c6d92fed96200347f06b7c5e1a3a28fa258ef7c">
        验证错误信息
            #{ifError},#{ifErrors}
                #{ifError}标签可以判断指定的作用域中是否有action添加的验证错误的信息
                #{ifErrors}只要模板中有任何的验证错误信息,都可以将其输出
                示例
                    #{ifError 'user.name'}
                      <p>
                        User name is invalid: 
                        #{error 'user.name' /}
                      <p>
                    #{/ifError}
                    --------------------------------
                    #{ifErrors}
                      <p>Error(s) found!</p>
                    #{/ifErrors}
            #{error},#{errorClass},#{errors}
                #{error}标签的作用是输出验证后的错误消息
                示例
                    #{error 'user.name'/}
                #{errorClass}标签的作用是如果存在指定的验证错误消息,则标签在解析时将被替换为文本hasError,一般在class中使用
                示例
                    <input name="name" class="#{errorClass 'name'/}">
                    相当于<input name="name" class="${errors.forKey('name') ? 'hasError' : ''}">
                #{errors}标签可以遍历当前的验证错误对象集合,其使用方式与 #{list}非常类似,循环体中使用的对象变量名称是${error}
                示例
                    <ul>
                        #{errors}
                            <li>${error}</li>
                        #{/errors}
                    </ul>
    内置函数
        格式化日期
            ${new Date(1275910970000).format('dd MMMM yyyy hh:mm:ss')}
        格式化数字
            ${42.format('000.00')}
        字符串转义
            escape(),escapeHtml()   对字符串进行转义处理
            raw()                   跳过模板对字符串的自动转义处理
            urlEncode()             将URL查询字符串中用到的特殊符号转义输出
        数组操作(字符串数组)
            contains(string)        判断数组中是否包含指定元素
    内置对象
        所有添加到renderArgs作用域的对象,都会以变量的形式注入模板。
        默认的内置对象
            errors      验证错误信息结果集,对应play.data.validation.Validation.errors()
            flash       Flash作用域,对应play.mvc.Scope.Flash
            lang        当前使用的语言,对应play.i18n.Lang
            messages    消息映射变量,对应play.i18n.Messages
            out         引用页面输出流,对应java.io.PrintWriter
            params      当前参数变量,对应play.mvc.Scope.Params
            play        Play框架类,对应play.Play
            request     当前请求对象,对应play.mvc.Http.Request
            session     Session作用域,对应play.mvc.Scope.Session



域模型
    属性模拟
        在Play中把属性变量声明为public,并可以不提供set/get方法,可以使用等号为属性赋值
        运行时Play会自动生成相应的getXxx/setXxx方法(将声明为public的字段都视为属性)
        使用等号为属性进行赋值,也会在程序加载时自动转换为set方法
        如果存在自己定义的get/set方法,则play会优先选择手动编写的方法
    数据库配置
        在conf/application.conf文件中添加
            db.url=jdbc:mysql://localhost/test
            db.driver=com.mysql.jdbc.Driver
            db.user=root
            db.pass=123456
        可选配置方言
            jpa.dialect=<dialect>
        获取Connection对象
            Connection conn = play.db.DB.getConnection();
            conn.createStatement().execute("select * from products");
    数据持久化
        Play的持久层框架采用的是Hibernate,使用Hibernate(通过JPA)自动地将Java对象持久化到数据库
        在实体类上增加@javax.persistence.Entity注解后,Play会自动为其开启JPA实体管理器。
        得到实体管理器
            EntityManager em = JPA.em();
        Play提供的play.db.jpa.Model类提供了对模型自己进行增删改查的便捷方法,可以使用实体类继承



Job异步处理
    概述
        Job就是需要在指定的时刻或者时间段内执行的任务,通常由作业调度程序来执行调度和管理。
        需要继承play.jobs.Job类,没有返回值就重写doJob方法,有返回值就重写doJobWithResult方法
        job必须在单独的一个文件中,不能有同级的类,job不能访问其它类的方法?
    在类声明上添加注解(开发模式先访问一下才会开始执行)
        @OnApplicationStart     在应用程序启动时执行
        @OnApplicationStop      在应用程序停止时执行
        @Every                  周期性执行任务,例@Every("1h"),可选单位d,h,min,mn,s (min和mn都为分钟)
        @On                     在具体的某个时间点执行,例@On("0 0 12 * * ?")
    CRON表达式
        @On标签中使用的是Quartz库的CRON表达式。
        CRON表达式是由7个子表达式组成的字符串,每个子表达式都描述了单独的日程细节。
        这些子表达式用空格分隔,分别表示:
            Seconds 秒
            Minutes 分钟
            Hours 小时
            Day-of-Month 一个月中的某一天
            Month 月
            Day-of-Week 一周中的某一天
            Year 年(可选)
        具体CRON表达式的例子:"0 0 12 ? * WED",表示“每周三的中午12:00”。
    手动调用job
        创建实现类的对象,并使用now()方法即可,可返还一个结果,默认为异步执行,如果想阻塞线程,可以调用结果的get方法
        every(str)可手动启动job,并指定间隔时间



JPA支持
    启用JPA实体管理
        Play会自动查找标记为javax.persistence.Entity注解的类
        @javax.persistence.Entity注解的作用是通知Play对当前实体类进行管理
    获得JPA实体管理器
        JPA.em()
    事务管理
        Play会自动进行事务管理,在每次发送HTTP请求时自动开启事务,发送HTTP响应完毕后提交事务。
        Play为事务管理控制提供了注解的支持。
        如果需要将Action声明为只读事务,可以在Action方法上标记@play.db.jpa.Transactional(readOnly=true)注解;
        如果执行当前方法时无需开启事务,可以在Action方法上标记@play.db.jpa.NoTransaction;
        如果当前控制器中所有方法都无需开启事务,可以直接在控制器上标明@play.db.jpa.NoTransaction。
        


数据验证
    使用验证器进行数据验证
        框架为每个请求绑定了验证器。应用代码中可以通过以下三种方式对数据进行验证
            1.使用API
                public static void hello(String name) {
                     validation.required(name);  //验证name参数是否被赋值
                }
            2.在方法中添加注解
                public static void hello(@Required String name){
                }
            3.在定义域模型时添加注解
                public class User extends Model {
                   @Required
                   public String name;  //为User的name属性添加@Required验证
                } 
                //直接使用@Valid注解对POJO参数进行验证
                public static void save(@Valid User user) { 
                }
        当验证不通过时,验证器会将错误以play.data.validation.Error的形式保存,因此每个验证器以集合的形式维护了一系列的error对象。每个error对象有key和message两个属性:
            key:该属性帮助我们标识引起错误的元素。key的值可以任意设定,默认与验证的数据同名。
            message:验证消息,用于描述验证不通过的错误信息。message可以是纯文本或指向消息包(message bundle)的key(通常为了支持国际化)。
        在验证结束后我们可以检查是否有error产生,并将验证消息打印出来
            public static void hello(String name, Integer age) {
                 validation.required(name);
                 validation.required(age);
                 validation.min(age, 0);
                 if(validation.hasErrors()) {                
                     for(Error error : validation.errors()) {
                         System.out.println(error.message());
                     }
                 }
            }
        方法说明
            error.message()     返回此error对象的message
            error.message(str)  设置此error对象的key,然后返回此对象的message(如果没有在配置文件中找到指定key,则直接返回key)
    设置错误消息提示
        如果validation.required(name);校验失败,则error.message()的值为"Required",可以自定义错误消息提示
        Play提供了三种方式自定义错误消息提示:
            在项目的conf/messages文件中自定义某一类错误校验的提示消息
            在项目的conf/messages文件中自定义错误校验的提示消息,并为error设置key
            直接为error设置message
        自定义某一类错误校验的提示消息
            在conf/messages中修改
                不带参数: validation.required=Please enter a value      
                含有参数: validation.required=%s is required                # %s自动替换为 error.key
        自定义错误验证消息(手动使用)
            在conf/messages中添加
                validation.required.em = You must enter the %s!
            使用(三种方法都可以)
                1.使用API设置key
                    validation.required(manualKey).message("validation.required.em")
                2.使用注解设置key
                    public static void hello(@Required(message="validation.required.em") String name) { 
                    } 
                3.JavaBean的属性中设置key
                    public class Person extends Model { 
                       @Required(message = "validation.required.em")
                       public String name; 
                    } 
                    public static void hello(@Valid Person person) {
                    } 
        直接为error设置message
            使用方法与自定义错误验证消息相同,只是不在conf/messages文件中添加配置
            原因
                error.message(str)方法的作用为 返回此对象的message,如果没有在配置文件中找到指定key,则直接返回key
                所以,通过error.message(str)方法设置key的值,并且不在配置文件中添加配置.相当于直接设置了message的值
        注意
            在conf/message文件中配置时,如果消息提示中,需要获取 验证方式的参数
                使用 %2$s  %3$s 依次获取(数字为 %2$d  %3$d)
            如范围验证
                validation.range=Not in the range %2$d through %3$d
    在页面显示错误消息提示
        示例
            #{ifErrors}
               <h1>Oops...</h1>
               #{errors}
                   <li>${error}</li>
               #{/errors}
            #{/ifErrors}
            #{else}
               Hello ${name}, you are ${age}.
            #{/else}
    完整示例
        控制器
            提供两个action方法,一个用于返回表单(index),一个用于接收提交的表单内容(hello)
                public static void index() {
                    render();
                }
                public static void hello(String name, Integer age) {
                    validation.required(name);
                    validation.required(age);
                    validation.min(age, 0);
                    if(validation.hasErrors()) {
                        params.flash();     // 将HTTP参数保存在Flash作用域中
                        validation.keep();  // 在下一个请求中保持错误信息的集合
                        index();
                    }
                    render(name, age);
                }
        页面
            #{form @Application.hello()}
               <div>
                  Name: <input type="text" name="name" value="${flash.name}" />
                  <span class="error">#{error 'name' /}</span>
               </div>
               <div>
                  Age: <input type="text" name="age" value="${flash.age}" /> 
                  <span class="error">#{error 'age' /}</span>
               </div>
               <div>
                  <input type="submit" value="Say hello" /> 
               </div>
            #{/form}
    内置验证方式
        required        验证是否为空
        equals          验证两个值是否相等
        range           验证是否在给定的两个数值范围内
        phone           验证是否为合法的电话号码
        email           验证E-mail地址是否合法
        url             验证是否为合法的URL
        match           验证是否匹配给定的正则表达式
        
        max             验证数值大小是否大于给定的值
        min             验证数值大小是否小于给定的值
        maxSize         验证字符串长度是否大于给定的值
        minSize         验证字符串长度是否小于给定的值
        
        future          验证是否为相对未来的时间
        past            验证是否为相对过去的时间
        ipv4Address     验证是否为符合ipv4规则的IP地址
        ipv6Address     验证是否为符合ipv6规则的IP地址
        isTrue          验证String或者Boolean类型变量是否为true
    自定义验证
        步骤
            自定义实现类,继承Check类,重写isSatisfied方法
            使用@CheckWith注解,在注解中传递实现类的class对象
        示例
            public class User {
                @Required
                @CheckWith(MyPasswordCheck.class)
                public String password;
                
                static class MyPasswordCheck extends Check {
                    public boolean isSatisfied(Object user, Object password) {
                        return notMatchPreviousPasswords(password);
                    }
                }
            }
        


缓存
    概述
        缓存是用来避免频繁到服务器端获取数据而建立的一个存取更快的临时存储器。缓存的容量相对较小,但执行速度非常快,其主要作用为:
            存储系统经常访问的数据。
            存储耗时较长的计算结果。
        当我们在使用缓存时,必须明确其自身特性:
            缓存存在于内存中(不进行持久化),只是用于存放暂时性的数据,时间一到就会过期。
            因此缓存并不是一个安全的存储器,不能保证数据可以永久存在。
            如果发现数据在缓存中已过期,需要重新获取数据,并再次放入缓存
        与Session对象不同,缓存中的内容是独立的,不会绑定任何特定的用户。所以一般使用session.getId作为key将数据存入缓存
    API
        Cache.get(id, Product.class)
        Cache.set("product_"+id, product, "30mn");
        Cache.delete("product_"+id);
        
        Cache.safeGet(id, Product.class)
        Cache.safeSet("product_"+id, product, "30mn");
        Cache.safeEelete("product_"+id);
        注意:
            带safe前缀的方法是阻塞的,而标准方法是非阻塞的
            delete方法会立即返回结果,并没有等待缓存对象是否被真正地物理删除。
            因此,如果程序执行期间发生了错误(例如IO错误),缓存对象可能仍然存在,并没有被删除。
            如果操作需要确保缓存对象被删除,可以使用safeDelete方法,该方法是阻塞式的,并返回一个布尔值标识对象是否被成功删除。
        示例
            public static void showProduct(String id) {
                Product product = Cache.get(id, Product.class);
                if(product == null) {
                    product = Product.findById(id);
                    Cache.set("product_"+id, product, "30mn");
                }
                render(product);
            }
             
            public static void addProduct(String name, int price) {
                Product product = new Product(name, price);
                product.save();
                showProduct(id);
            }
             
            public static void editProduct(String id, String name, int price) {
                Product product = Product.findById(id);
                product.name = name;
                product.price = price;
                Cache.set("product_"+id, product, "30mn");
                showProduct(id);
            }
             
            public static void deleteProduct(String id) {
                Product product = Product.findById(id);
                product.delete();
                Cache.delete("product_"+id);
                allProducts();
            }
    配置Memcached
        如果项目要启用Memcached,需要在application.conf中打开Memcached开关,并设置Memcached的守护进程地址:
            memcached=enabled
            memcached.host=127.0.0.1:11211
        我们还可以指定多个守护进程地址,使之连接到同一个分布式缓存:
            memcached=enabled
            memcached.1.host=127.0.0.1:11211
            memcached.2.host=127.0.0.1:11212



常用
    Play.applicationPath.getPath()      获得项目路径
    Request.current()                   获取request对象
    Response.current()                  获取response对象


MVC应用程序模型 - 7 - app/controllers - 8 - app/models - 8 - app/views - 8 - 请求生命周期 - 8 - 标准应用程序布局layout - 9 - app目录 - 9 - public目录 - 10 - conf目录 - 10 - lib目录 - 11 - 开发生命周期 - 11 - 连接到java调试器 - 12 - 类增强Enhancement - 13 - 02.HTTP路由 - 13 - 关于REST - 14 - routes文件语法 - 14 - HTTP方法 - 15 - URI范示 Pattern - 15 - Java调用定义 - 17 - 把404当作action来用 - 17 - 指派静态参数 - 17 - 变量和脚本 - 18 - 路由优先级 - 18 - 服务器静态资源 - 18 - staticDir: mapping - 18 - staticFile: mapping - 19 - URL 编码 - 19 - 反转路由:用于生成某些URL - 19 - 设置内容风格(CSS) - 20 - HTTP 内容协商 negotiation - 21 - 从http headers开始设置内容类型 - 21 - 定制格式 - 22 - 03.控制器 - 23 - 控制器概览 - 23 - 获取http参数 - 24 - 使用params map - 25 - 还可以从action方法签名实现转换 - 25 - 高级HTTP Java绑定 - 26 - 简单类型 - 26 - Date类型 - 26 - Calendar日历 - 27 - File - 27 - 支持类型的数组或集合 - 28 - POJO对象绑定 - 29 - JPA 对象绑定 - 30 - 定制绑定 - 30 - @play.data.binding.As - 30 - @play.data.binding.NoBinding - 31 - play.data.binding.TypeBinder - 31 - @play.data.binding.Global - 32 - 结果类型 - 32 - 返回一些文本类型的内容 - 33 - 返回一个JSON字符串 - 33 - 返回一个XML字符串F - 34 - 返回二进制内容 - 34 - 作为附件下载文件 - 34 - 执行一个模板 - 35 - 跳转到其他URL - 36 - Action链 - 36 - 定制web编码 - 37 - 拦截器 - 38 - @Before - 38 - @After - 39 - @Catch - 40 - @Finally - 41 - 控制器继承 - 42 - 使用@With注释添加更多的拦截器 - 42 - Because Java does not allow multiple inheritance, it can be very limiting to rely on the Controller hierarchy to apply interceptors. But you can define some interceptors in a totally different class, and link them with any controller using the @With annotation.由于java不允许多继承,通过控制器继承特点来应用拦截器就受到极大的限制。但是我们可以在一个完全不同的类里定义一些拦截器,然后在任何控制器里使用@With注释来链接他们。 - 42 - Session和Flash作用域 - 42 - 04.模板引擎 - 43 - 模板语法 - 43 - Expressions: ${…} - 44 - Template decorators : #{extends /} and #{doLayout /} - 44 - Tags: #{tagName /} - 45 - Actions: @{…} or @@{…} - 46 - Messages: &{…} - 46 - Comment: *{…}* - 46 - Scripts: %{…}% - 46 - Template inheritance继承 - 47 - 定制模板标签 - 48 - 检索tag参数 - 48 - 调用标签体 - 48 - 格式化特定标签 - 49 - 定制java标签 - 49 - 标签命名空间 - 50 - 在模板里的Java对象扩展 - 51 - 创建定制扩展 - 52 - 模板里可以使用的保留对象 - 52 - 05.用play验证http数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值