首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 文件上传和 Atom 联合(1)

精通 Grails 文件上传和 Atom 联合(1)

修复隐藏的 bug启动 Grails 时,grails-app/conf/Bootstrap.groovy 增加 2 个用户和 4 个新的博客条目。但是,如果尝试通过 Web 界面增加博客条目,会发生什么?可以使用下面的步骤试试看:
  • 以用户名  jsmith 和密码 wordpass 登录。
  • 单击 New Entry
  • 添加标题和摘要。
  • 单击 Create
您将看到以下错误:Property [author] of class [class Entry] cannot be null。那么,这个 bug 是如何引入到应用程序中的?毕竟,bootstrap 代码还能正常工作。
在第一篇 Blogito 文章(“”)中,我让您通过输入 grails generate-views Entry 生成 Groovy Server Pages(GSP)视图。在随后的文章中,我更改了 domain 类,但是从未让您再回过头来生成视图。当我添加 Entry 与 User 之间的 1:M 关系时,磁盘上的 create.gsp 视图一直不变,如清单 1 所示。(还记得吗,belongsTo 创建一个名为 author 的字段,该字段的类型为 User)。
清单 1. 打破 GSP 的 1:M 关系
1
2
3
4
5
6
7
8
class Entry {
  static belongsTo = [author:User]

  String title
  String summary
  Date dateCreated
  Date lastUpdated
}




不得不说,要使一切同步,最安全的方式还是通过动态脚手架生成视图 — 特别是在开发的早期,域模型不断变化的时候,更是如此。当然,不能仅仅依靠通过脚手架生成的视图,但是,当您在磁盘上生成 GSP 时,使它们保持最新的责任就从 Grails 转移到您自己身上。
如果现在为 Entry 类生成视图的话,Grails 会提供一个组合框,其中显示一个 Author 列表,如清单 2 所示。您自己不要 这样做 — 这只是为了演示。稍后我将提供两种不同的选项。
清单 2. 为 1:M 关系生成的组合框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<g:form action="save" method="post" >
  <div class="dialog">
    <table>
      <tbody>
        <!-- SNIP -->
        <tr class="prop">
          <td valign="top" class="name">
            <label for="author">Author:</label>
          </td>
          <td valign="top"
              class="value ${hasErrors(bean:entryInstance,
                                       field:'author','errors')}">
            <g:select optionKey="id"
                      from="${User.list()}"
                      name="author.id"
                      value="${entryInstance?.author?.id}" ></g:select>
          </td>
        </tr>
        <!-- SNIP -->
      </tbody>
    </table>
  </div>
</g:form>




注意 <g:select> 元素。字段名为 author.id。在 “” 中可以了解到,列表中显示的文本来自 User.toString() 方法。该文本通常也是表单提交时作为字段值发回到服务器的值。在这里,optionKey 属性覆盖字段值,从而发回 Author 的 id。(要了解更多关于 <g:select> 标记的信息,请参阅 )。
为 EntryController.groovy 提供 author.id 字段的最快方式是将一个隐藏字段添加到表单中,如清单 3 所示。由于执行 create 动作前必须登录,而登录的 User 是博客条目的 author,因此对于这个值可以安全地使用 session.user.id。
清单 3. 从表单传递 author.id 字段
1
2
3
4
<g:form action="save" method="post" >
  <input type="hidden" name="author.id" value="${session.user.id}" />
  <!-- SNIP -->
</g:form>




对于像 Blogito 这样的简单的应用程序,这样也许就足够了。但是,这样做留下了一个漏洞,使客户端的黑客有机会为 author.id 注入不同的值。为确保彻底的安全,可以在 save 闭包中添加 Entry.author,如清单 4 所示:
清单 4. 将 author.id 保存在服务器上
1
2
3
4
5
6
7
8
9
10
11
def save = {
    def entryInstance = new Entry(params)
    entryInstance.author = User.get(session.user.id)
    if(!entryInstance.hasErrors() && entryInstance.save()) {
        flash.message = "Entry ${entryInstance.id} created"
        redirect(action:show,id:entryInstance.id)
    }
    else {
        render(view:'create',model:[entryInstance:entryInstance])
    }
}




这是生成控制器时得到的标准 save 闭包,再加上一行定制的代码。entryInstance.author 行根据 session.user.id 值从数据库获取 User,并填充 Entry.author 字段。
在下一节中,您将定制 save 闭包,以处理文件上传,所以您仍可能在安全性方面犯错误,将  中的代码添加到 EntryController.groovy 中。重新启动 Grails,确保可以通过 HTML 表单成功地添加新的 Entry。
返回列表