Play Framework 工程目录结构

来源:互联网 发布:元数据由什么存储 编辑:程序博客网 时间:2024/06/16 16:41

Play 工程目录结构

web_app                         根目录                |   sbt                         SBT Unix 批处理脚本用于启动sbt-launch.jar |   sbt.bat                     SBT Windows 批处理脚本用于启动sbt-launch.jar |   sbt-launch.jar              SBT 启动的Java可执行类库|+---app                         Play Web 应用全部代码所在目录|   ||   +---models                  模型代码所在目录|   |       Message.scala       留言板例程模型代码|   ||   +---controllers             控制器代码所在目录|   |       Application.scala   默认控制器代码|   ||   \---views                   视图(Play Scala HTML模板) 代码所在目录|           main.scala.html     主模板文件|           index.scala.html    首页模板文件|           msgboard.scala.html 留言板例程模板文件|+---conf                        Play 配置文件所在目录|       application.conf        应用配置文件|       routes                  应用入口路由文件,所有的HTTP请求将通过该文件转发到指定的Scala对象处理|+---logs                        日志目录|       application.log         应用运行日志|+---project                     SBT工程文件|       build.properties        保存所需的SBT版本信息,通常无需更改|       Build.scala             主要的工程配置文件|       plugins.sbt             告知SBT本工程所需要的插件以及下载位置|+---public                      存储一切直接发送给浏览器的资源文件|   ||   +---images                  图像文件,如JPEG、PNG、GIF等|   ||   +---javascripts             JavaScript脚本文件|   ||   \---stylesheets             CSS样式表文件|\---target                      存放编译后的可执行代码和编译时的中间代码

一切从 routes 开始

Play 的应用入口为 conf/routes 文件,该文件定义了全部Play应用中URL对应的动作(Action),如当浏览器请求访问 http://localhost:9000/ ,Play 应用将会返回一个页面,此时routes文件应定义成如下形式:

GET     /                           controllers.Application.index

该定义告知Play收到HTTP GET类型请求且路径为/时调用controllers包中Application类的index方法,对应的代码即为:web_app/app/controllers/Application.scala

此时若尝试访问 http://localhost:9000/index.html,会出现“Action not found”错误提示,因为此时没有在routes文件中定义针对GET /index.html的对应动作,开发者可尝试修改routes文件,添加下列行:

GET     /index.html                           controllers.Application.index

当再次访问 http://localhost:9000/index.html 会就发现浏览器可以正常显示与http://localhost:9000/一样的内容。

理解Play Framework控制代码 (Controller)

上文的routes定义//index.html对应了Application.scala代码中的index方法来显示网页内容:

//所有的控制代码按Play规范均归入controllers包package controllers//导入Play应用开发所需的类库import play.api._import play.api.mvc._import play.api.templates._//Application全局对象实例化,因此使用Object来声明Application并继承Play的Controller类object Application extends Controller {//定义index方法,任何routes文件中指定调用的方法,必须返回Action对象来处理HTTP请求    def index = Action {      //任何Action对象必须获得反返回的Result对象      //Ok继承于Result对象,所以返回Ok表示其包含的内容为HTTP 200 OK状态      //在Scala最后一行代码可以无需使用return来返回结果      //因此下面最后一行代码等同于 return Ok(views.html.index("首页","首个Play Web应用!"))      Ok(views.html.index("首页","第一个Play Web应用!"))    }}

Ok表示所包括的内容的状态为HTTP 200 OK,现在尝试修改该段代码为:

Ok("<p>修改后的<strong>代码!</strong></p>")

保存并刷新浏览器,会发现浏览器会将上面的字符串连同HTML标签一起显示出来,因为此时传入Ok中对象类型为String,Ok将其Content-type作为text/plain输出,所以连同HTML标签一起显示。

因此需要返回并告知Ok这是段Html格式内容而非纯文本,因此套用Html类,再次修改代码为:

Ok(Html("<p>修改后的<strong>代码!</strong></p>"))

保存并刷新浏览器,HTML标签已不存在,文字也按照规定的格式正常显示。

由此可知Play Web应用的调用顺序和关系为:

浏览器 ( http://localhost:9000/ )-> Play 框架 (conf/routes) -> 对应的Controller代码 (app/controllers/Application.scala) -> 对应的返回Action (def index = Action {...}) 的方法 -> 对应的可返回Result的代码 (OK(...)) -> 要返回的正文内容 ( "..." 纯文本 或 Html("...) HTML格式) 。

Play Framework模板 (View)

Play的模板在HTML基础上直接基于Scala语言,模板文件通常存放在/app/views目录下,文件须以“.scala.html”双扩展名命名。Play的每个模板文件其实都是一个Scala代码,均需要通过Scala编译器检查其类型与语法,并编译成.class可执行的JVM二进制文件。

编译时Play首相会将.scala.html的Play模板文件自动生成为.scala的源代码文件,如 /app/views/index.scala.html的模板文件将会生成/target/scala-2.9.1/src_managed/main/views/html/index.template.scala文件,该文件将会继而被Scala编译器编译成index.class。

首先看一下index.scala.html文件:

@(title: String, message: String)@* 模板入参,两个参数均为String类型,分别命名为title和message *@@* 调用main.scala.html模板,传入参数title *@@main(title) {    @* 输出参数message *@    <p>@message</p>    <a href="#">Scala语言与Play框架入门教程</a>}

在Play的模板中@符号代表跟随其后的代码为Scala语言代码,由于最终任何.scala.html文件都将转换成Scala语言代码,在编写Play模板时也可以理解为编写Scala语言代码,一个模板文件可以理解为一个对象,因此需要传入参数时就必须在模板文件的第一行定义都需要哪些入参以及这些入参的名字和类型。服务器端的代码注释写法为: @* 注释内容 *@

比如index.scala.html定义需要两个入参,两个参数均为String类型,分别命名为title和message,就对应了Application.scala代码:

Ok(views.html.index("首页","第一个Play Web应用!"))

Play将会生成一个包名为views.html,名为index的实例化对象,并接受两个String类型的入参,此时index.scala.html文件中的title = "首页"、message = "第一个Play Web应用!",大括号中此时的内容应为:

<p>第一个Play Web应用!</p><a href="#">Scala语言与Play框架入门教程</a>

接下来@main(title)调用了main.scala.html模板,在Scala代码层面可以理解为views.html.main(title)。

再看main.scala.html文件:

@(title: String)(body: Html)<!DOCTYPE html><html>    <head>        <title>@title</title>    </head>    <body>        @body    </body></html>

main.scala.html声明需要两个分别名为title的String类型和名为body的Html类型入参,index.scala.html将title直接传给了main,因此此时main的title的值同样也是"首页",其后index将大括号中的全部的内容传递给了main的body。

根据上述代码当我们访问http://localhost:9000/时,输出的全部内容为:

<!DOCTYPE html><html>    <head>        <title>首页</title>    </head>    <body>    <p>第一个Play Web应用!</p>    <a href="#">Scala语言与Play框架入门教程</a>    </body></html>

Play 业务和数据模型 (Model)

当Web应用需要对数据模型进行处理时,则需要在app/models目录下编写相应的scala代码,这些模型可以与内存、文件、关系型数据库或NoSQL非关系型数据库等数据存储方式进行绑定,也可以是对业务逻辑或表单的定义。

下文将会对Play模型进行讲解。

设计一个留言板,通过表单提交留言,存储在内存中,并显示出全部留言。

定义留言板表单和数据模型

在app/models目录下创建Message.scala文件,并写入如下Scala代码:

package modelsimport play.api._import play.api.data._import play.api.data.Forms._//每条留言的数据模型:包括两个字符串,分别存储名字和内容case class Message(name: String, content: String)//全部留言的数据操作object Message {  //用于存储全部留言的列表  var list: List[Message] = Nil  //将留言追加在用于存储全部留言的列表前面  def post(name: String, content: String) {        list :::=  List(Message(name, content))            }  //定义表单及其校验要求,nonEmptyText表示该项内容不得为空  val form = Form(tuple(      "name" -> nonEmptyText,         "content" -> nonEmptyText  ))}

设计视图模板 (View)

在app/views目录下创建msgboard.scala.html文件,并写入如下Play模板代码:

@(msgs: List[Message], msgForm: Form[(String, String)])@*模板入参:第一个名为msgs的List[Message]类型参数;第二个名为msgForm的Form[(String, String)]类型参数*@@* 导入 helper 包下的类和对象,因为需要其中的form来生成表单 *@@import helper._@* 调用main.scala.html模板,并将标题改为"留言板" *@@main("留言板") { <h2>留言<h2> <ul>        @* 提取 msgs 入参中的全部数据 *@        @msgs.map { message =>            <li>                @* 提取名字 *@                <p><strong>@message.name</strong></p>                @* 提取内容 *@                <p>@message.content</p>            </li>        } </ul>    <h2>发言</h2>    @* 创建表单,告知表单提交时发送POST给routes.Application.postMsg来处理 *@    @form(routes.Application.postMsg) {        @* 生成名字的输入框*@        @inputText(msgForm("name"), '_label -> "名字")         @* 生成内容的输入框 *@        @inputText(msgForm("content"), '_label -> "内容")         @* 生成用于提交的按钮 *@        <input type="submit" value="发送">    }}

编写控制器 (Controller)

在app/controllers/Application.scala文件中加入如下代码:

 //显示留言列表和发言表单  def m = Action {    Ok(views.html.msgboard(Message.list, Message.form))  }  //处理发言  def postMsg = Action { implicit request =>  Message.form.bindFromRequest.fold(    //处理错误    errors => BadRequest(views.html.msgboard(Message.list, errors)),    {      case (name, content) =>      //发言      Message.post(name, content)      //重新定向到显示留言列表和发言表单页面      Redirect(routes.Application.m)    }  )

定义routes

在conf/routes文件中写入如下行:

# 显示留言列表和发言表单GET     /m                          controllers.Application.m# 使用POST方式提交留言POST    /postMsg                    controllers.Application.postMsg

运行和测试留言板

此时在浏览器中输入:http://localhost:9000/m,即可测试留言板。

目前全部留言以List[Message]类型存于内存之中,一旦停止Play应用全部留言数据将会消失,下次再"sbt run"该留言板Play Web应用时留言内容为空。

建议学习本文的开发者尝试将留言内容存于本地文件或数据库中。

(完)

0 0