Play中的play.libs.Images类提供了生成验证码的支持,操作也非常简单。我们可以通过静态方法Images.captacha()快速生成默认大小为150*50的验证码图片,也可以使用Images.captacha(int width, int height)方法生成指定大小的验证码图片。
在Application控制器中增加captcha Action:
public static void captcha(){
Images.Captcha captcha=Images.captcha();
renderBinary(captcha);
}
Action定义完后,在路由文件中配置验证码的路由规则:
GET /captcha Application.captcha
打开http://localhost:9000/captcha查看验证码。每次刷新浏览器,都会生成随机的验证码。
上例生成的验证码还是存在安全隐患的。我们通常会为验证码添加适当的噪点,降低验证码被程序识别的可能性。我们可以使用addNoise()方法为验证码添加默认为黑色的噪点,也可以使用addNoise(java.lang.String color)方法指定噪点的颜色。
修改Application控制器中的captcha Action:
public static void captcha(){
Images.Captcha captcha=Images.captcha();
captcha.addNoise();
renderBinary(captcha);
}
play.libs.Images提供的setBackground(java.lang.String color)方法可以为验证码设置背景色,下例将验证码背景色设置为#E4EAFD。
修改Application控制器中的captcha Action:
public static void captcha(){
Images.Captcha captcha=Images.captcha();
captcha.addNoise();
captcha.setBackground("#E4EAFD");
renderBinary(captcha);
}
此外还有为验证码添加渐变背景色的setBackground(java.lang.String from, java.lang.String to)方法;为验证码添加波浪曲线背景的setSquigglesBackground()方法,本节就不展开介绍了。
验证码中唯一的信息就是文本内容,通过getText()方法可以获取验证码中的文本内容。我们也可以通过getText(java.lang.String color)方法在获取文本内容的同时设置验证码中文本的颜色。
修改Application控制器中的captcha Action:
public static void captcha(){
Images.Captcha captcha=Images.captcha();
captcha.addNoise();
String code = captcha.getText("#ABCDEF");
captcha.setBackground("#E4EAFD");
renderBinary(captcha);
}
2、配置Log
Play内置的日志记录器是在Log4j之上的封装。Log4j的配置之简单使它遍及于越来越多的项目中,在开发中使用Log4j的好处主要可以归纳为以下几个方面:
Log4j是Apache的一个开放源代码项目。
play.Logger是Play的默认日志记录器,该日志记录器通过Log4j记录消息和异常。下例为记录简单的程序日志:
Logger.info("A log message");
Logger.error(ex, "Oops");
play.Logger类支持标准的Java格式化语法:
Logger.debug("The param was %s", param);
Logger.info("I want to log %s and %s and %s", a, b, c);
特定需求下,我们仍然可以直接使用Log4j来创建其他记录器:
org.apache.log4j.Logger.getLogger("another.logger");
通过修改application.conf配置可以更改play.Logger的日志记录级别:
application.log=INFO
修改此值无需重新启动服务器就可生效。此日志级别配置只针对应用程序产生的消息有效。
如果读者需要充分配置Log4j,我们可以在conf/目录下创建log4j.properties文件。由于conf目录是在classpath路径配置中的第一个元素,所以这个配置文件将会被所有库使用。标准Log4j配置如下:
log4j.rootLogger=ERROR, Console
log4j.logger.play=INFO
# Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} %-5p ~ %m%n
读者可以参考默认配置并在此基础上根据自己的需求进行修改。
Log4j包含三个重要的组件,分别为Logger,Appender以及Layout。Logger被称作日志记录器,用于记录日志的类别;Appender可以被理解为输出源,即日志所输出的位置;Layout是日志布局,体现为日志以何种形式输出。
Logger
Logger(日志记录器)是日志处理的核心组件,负责处理日志记录的大部分操作。日志级别分为多种,从低到高分为ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF,当然也可以自定义日志级别。标准的日志级别粒度划分如下:
Level ALL:所有信息。
Level TRACE:级别为细粒度的信息。
Level DEBUG: 级别为细粒度的信息,粒度粗于TRACE,该级别打印的信息有助于调试应用程序。
Level INFO: 级别为粗粒度的信息,该级别日志主要用于记录程序运行过程的信息。
Level WARN:警告信息,预报潜在危险。
Level ERROR:错误信息,但应用程序仍可继续运行。
Level FATAL:严重错误信息,往往会造成应用程序的退出。
Level OFF:不打印任何信息。
Log4j建议只使用其中的四个级别,因为这些已经可以满足绝大部分应用的需求,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。
根Logger配置语法为: log4j.rootLogger = [ level ] , [ appenderName],[ appenderName], ...
具体配置写法如下:
log4j.rootLogger=INFO,Console,DailyRolling
Appender
Appender(输出源)负责控制日志记录的输出,其语法为:log4j.appender.[ appenderName ] = fully.qualified.name.of.appender.class
其中appenderName在Logger中定义(如Console和DailyRolling),Log4j提供多种fully.qualified.name.of.appender.class:
org.apache.log4j.ConsoleAppender:将日志信息打印到控制台。
org.apache.log4j.FileAppender:将日志信息打印到文件中。
org.apache.log4j.DailyRollingFileAppender:将日志信息打印到文件中,并每天产生一个文件。
org.apache.log4j.RollingFileAppender:将日志信息打印到文件中,文件大小到达指定尺寸时产生一个新文件。
具体配置写法如下:
#将日志信息打印到文件中,并每天产生一个文件
log4j.appender.DailyRolling=org.apache.log4j.DailyRollingFileAppender
具体配置写法如下:
#将日志信息文件以.log格式保存在logs/目录下,并以Oopsplay命名。
log4j.appender.DailyRolling.File = logs/Oopsplay.log
Layout
Layout提供了四种日志输出样式,读者可以根据需求格式化日志的输出,其语法为:log4j.appender.[ appenderName ].Layout= fully.qualified.name.of.layout.class。
Log4j为Layout提供多种fully.qualified.name.of.appender.class:
org.apache.log4j.HTMLLayout:日志信息采用HTML表格形式布局。
org.apache.log4j.PatternLayout:日志信息采用灵活的指定布局模式。
org.apache.log4j.SimpleLayout:日志中包含信息的级别和信息字符串。
org.apache.log4j.TTCCLayout:日志中包含信息产生的时间、线程、类别等信息。
具体配置写法如下:
#将日志信息采用灵活的指定布局模式
log4j.appender.R.layout=org.apache.log4j.PatternLayout
%m:输出代码中指定的消息。
%p:输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%r:输出自应用启动到输出该log信息耗费的毫秒数。
%c:输出所属的类目,通常就是所在类的全名。
%t:输出产生该日志事件的线程名。
%n:输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”。
%l:输出日志事件的发生位置。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss}。
其语法为:log4j.appender.[ appenderName ].Layout.[ option ] = [ value ]
具体配置写法如下:
log4j.appender.Console.layout.ConversionPattern=%d{ yyyy-MM-dd HH:mm:ss } %-5p %-20c ~ %m%n
配置实例
将日志输出到控制台:
log4j.rootLogger=INFO,Console
log4j.logger.play=INFO
#输出到控制台
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern= %d{ yyyy-MM-dd HH:mm:ss } %-5p %-20c ~ %m%n
将日志输出到文件(文件大小到达指定尺寸时产生一个新文件):
log4j.rootLogger=INFO,Rolling
log4j.logger.play=INFO
#输出到日志文件(文件大小到达指定尺寸时产生一个新文件)
log4j.appender.Rolling=org.apache.log4j.RollingFileAppender
log4j.appender.Rolling.File=logs/Oopaplay.log
#当日志文件大于1MB时生成新的日志文件,后缀可以是KB, MB 或者是GB
log4j.appender.Rolling.MaxFileSize=1MB
#指定可以产生的滚动文件的最大数
log4j.appender.Rolling.MaxBackupIndex=10
log4j.appender.Rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.Rolling.layout.ConversionPattern= %d{ yyyy-MM-dd HH:mm:ss } %-5p %-20c ~ %m%n
将日志输出到文件(每天产生一个文件):
log4j.rootLogger=INFO,DailyRolling
log4j.logger.play=INFO
#输出到日志文件(每天产生一个文件)
log4j.appender.DailyRolling = org.apache.log4j.DailyRollingFileAppender
log4j.appender.DailyRolling.File = logs/Oopaplay.log
log4j.appender.DailyRolling.layout = org.apache.log4j.PatternLayout
log4j.appender.DailyRolling.layout.ConversionPattern= %d{ yyyy-MM-dd HH:mm:ss } %-5p %-20c ~ %m%n
发送Email日志通知:
log4j.rootLogger=FATAL,MAIL
log4j.logger.play=INFO
#发送Email通知
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
#设置缓存,当日志记录数为5条时,发送邮件。默认为1
log4j.appender.MAIL.BufferSize=5
#邮箱登录名,以[email protected]为例
log4j.appender.MAIL.SMTPUsername=oopsplay
#邮箱登录密码,密码为“123”
log4j.appender.MAIL.SMTPPassword= 123
#以网易126邮箱服务器为例
log4j.appender.MAIL.SMTPHost=smtp.126.com
#邮件主题
log4j.appender.MAIL.Subject=FATAL LOG
#邮箱名
log4j.appender.MAIL.From=oopsplay@126.com
#目的邮箱,多个邮箱可用逗号分隔
log4j.appender.MAIL.To=dhl@oopsplay.org,zst@oopsplay.org
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern= %d{ yyyy-MM-dd HH:mm:ss } %-5p %-20c ~ %m%n
将日志记录到数据库中:
log4j.rootLogger=ERROR,DATEBASE
log4j.logger.play=INFO
#将日志记录数据库(以mysq数据库为例,数据库名为log,数据表名为ERROR_LOG,字段名为message)
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.Threshold=ERROR
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/log
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=root
log4j.appender.DATABASE.sql=INSERT INTO ERROR_LOG (message) VALUES ("[Oopsplay]%d{yyyy-MM-dd HH:mm:ss} %t %p- %m%n")
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[Oopsplay]%d{yyyy-MM-dd HH:mm:ss} %t %p- %m%n
3、
play id
(图3.1 play id命令定义ID)
之后在配置文件中我们可以使用ID作为配置项的前缀来区别配置:
application.name=Cool app
application.mode=dev
application.log=INFO
# id为gbo的配置
%gbo.application.log=DEBUG
%gbo.db=mem
# id为src的配置
%src.http.port=9500
# id为prod的配置
%prod.http.port=80
%prod.application.log=INFO
%prod.application.mode=prod
application.mode=dev
%production.application.mode=prod
通过以下命令使程序在产品模式下运行:
play run --%production
play test命令与play run --%test命令等价。
4、国际化
UTF-8是UNICODE的一种变长字符编码,又称万国码。并且已经标准化,可以显示所有语言的字符。
读者在开发Play应用时,请务必保持编码的一致:
读者可能会感到奇怪,为什么Play的配置文件(甚至一些Java属性文件)都不是*.properties后缀格式的。这是因为Java规定属性文件(properties files)必须为iso-8859-1编码,而Play只支持UTF-8一种编码格式。
为了使应用程序支持I18n,需要为不同区域的语言配置messages文件。我们可以直接在Play项目的conf/目录下创建messages文件(该文件实际就是Java属性文件),例如:
hello=Hello!
back=Back
也可以为不同语言指定不同的messages文件,并以ISO语言代码作为messages文件的扩展名。例如,创建conf/messages.zh文件,对应中文的国际化支持:
hello=你好!
back=后退
当我们创建了不同语言指定的messages文件之后,需要在conf/application.conf文件中进行配置,添加文件的引用:
application.langs=zh
&{'hello'}
我们可以在conf/application.conf文件中设置程序支持的语言列表,适应多种语言的国际化:
application.langs=fr,en,ja
当用户第一次发送请求时,Play会解析HTTP请求头中Accept-language的信息,识别用户所使用的语言。之后Play会将识别的语言信息保存至PLAY_LANG cookie,并在以后的请求中使用同样的语言进行处理。
我们可以采用“语言_国家”的命名形式区别那些多样化的语言(en_US,en_GB,zh_CN或者zh_TW)。比如:在某个应用程序中,大部分用户来自美国,但同时需要支持英国英语,比较好的定义方式是将“en”作为美国英语,将“en_GB”作为英国英语。
HTTP请求头中的Accept-language常常不包含国家信息,因此需要慎用这种“语言_国家”的形式定义语言。
我们可以在Java代码中直接使用play.i18n.Lang对象获得当前使用的语言:
String lang = Lang.get();
play.i18n.Lang辅助类提供的change()方法可以改变用户语言:
Lang.change("zh");
使用change()方法改变用户语言后,新的语言信息会保存在用户的language cookie中。
我们可以在application.conf文件中进行如下属性设置,定义项目的统一时间格式:
date.format=yyy-MM-dd
date.format.fr=dd/MM/yyyy
此时如果在视图模板中使用${date.format()}进行渲染,format()方法将参照此配置进行格式规定。如果date变量作为HTTP参数进行绑定,也遵循配置中的时间格式。
在程序代码中,我们可以使用play.i18n.Messages类获取messages文件中定义的内容:
public static void hello() {
renderText(Messages.get("hello"));
}
messages文件中的内容是可以使用标准的java.util.Formatter语法进行格式化的,因此当我们需要在messages文件中定义动态内容时,可以使用%s,%d等格式化输出:
hello=Hello %s!
如果messages文件中包含了动态内容,play.i18n.Messages类可以直接传递变量。修改hello Action:
public static void hello(String user) {
renderText(Messages.get("hello", user));
}
模板输出
当应用具备国际化支持后,我们可以在模板文件中使用 &{...} 显示本地化内容:
&{'hello'}
&{'hello', params.user}
多个参数
如果开发者在使用messages文件时需要使用多个传入参数,可以采用下例写法。该messages文件定义了guess的本地化操作,传入了两个十进制的integer参数。
guess=Please pick a number between %d and %d
在模板中就可以按照messages文件所定义的输出格式进行本地化操作了,但在使用时需要注意参数的顺序,具体使用方式如下。
&{'guess', low, high}
Please pick a number between 10 and 100
参数索引
在使用messages文件时,开发者可以通过索引定位,更加精确地使用传入参数,实现个性化的用法。下例代码展示了英文messages本地化的操作,包含了两个传入参数:
guess.characteristic=Guess %s’s %s.
定义的guess.characteristic包含两个传入参数,在模板中的具体使用如下:
&{'guess.characteristic', person.name, 'age'}
但是有些国家的语言并不是按照这个顺序的,比如法文就是按照相反的顺序书写这句话的。因此,我们在法文的本地化文件messages.fr中就需要使用索引,指定传入参数的顺序。
guess.characteristic=Devinez %2$s de %1$s.
Play还允许针对传入的age参数进行本地化操作,只需要在messages文件中使用&{...}标签即可。如果开发者希望改变模板中age的输出样式,可以将age作为key值,进行本地化设置,下例为messages.zh文件的写法:
guess.characteristic=猜猜%s的&{%s}。
age = 年龄
guess.characteristic=Devinez &{%2$s} de %1$s.
age = l’age
在messages文件中使用&{...}标签后,Play会按照key值进行遍历查找,获取相应的本地化内容。