精通 Grails: 文件上傳和 Atom 聯合

火星人 @ 2014-03-12 , reply:0
←手機掃碼閱讀

  
在這一期的 精通 Grails 中,Scott Davis 展示如何將文件上傳到 Grails 應用程序,並設置一個 Atom syndication feed。完成最後這些部分之後,Blogito 便成為一個完整的博客伺服器。

在過去幾期的 精通 Grails 文章中,您一直在逐步構建一個小型的博客服務(Blogito)。在這篇文章中,Blogito 將最終完工,成為一個實用的博客應用程序。您將為博客條目主體實現文件上傳功能,並添加自己製作的用於聚合的 Atom feed。

關於本系列

Grails 是一種新型 Web 開發框架,它將常見的 Spring 和 Hibernate 等 Java 技術與當前流行的約定優於配置等實踐相結合。Grails 是用 Groovy 編寫的,它可以提供與遺留 Java 代碼的無縫集成,同時還可以加入腳本編程語言的靈活性和動態性。學習完 Grails 之後,您將徹底改變看待 Web 開發的方式。

但是,在開始之前,請注意在上一篇文章(“身份驗證和授權”)中,我加入的認證使 UI 中出現一個細小的 bug。在加入新的特性之前,應該修復這個 bug。

修復隱藏的 bug

啟動 Grails 時,grails-app/conf/Bootstrap.groovy 增加 2 個用戶和 4 個新的博客條目。但是,如果嘗試通過 Web 界面增加博客條目,會發生什麼?可以使用下面的步驟試試看:

  1. 以用戶名 jsmith 和密碼 wordpass 登錄。
  2. 單擊 New Entry。
  3. 添加標題和摘要。
  4. 單擊 Create。

您將看到以下錯誤:Property [author] of class [class Entry] cannot be null。那麼,這個 bug 是如何引入到應用程序中的?畢竟,bootstrap 代碼還能正常工作。

在第一篇 Blogito 文章(“改變 Grails 應用程序的外觀”)中,我讓您通過輸入 grails generate-views Entry 生成 Groovy Server Pages(GSP)視圖。在隨後的文章中,我更改了 domain 類,但是從未讓您再回過頭來生成視圖。當我添加 Entry 與 User 之間的 1:M 關係時,磁碟上的 create.gsp 視圖一直不變,如清單 1 所示。(還記得嗎,belongsTo 創建一個名為 author 的欄位,該欄位的類型為 User)。


清單 1. 打破 GSP 的 1:M 關係
				  class Entry {    static belongsTo = [author:User]      String title    String summary    Date dateCreated    Date lastUpdated  }  

不得不說,要使一切同步,最安全的方式還是通過動態腳手架生成視圖 — 特別是在開發的早期,域模型不斷變化的時候,更是如此。當然,不能僅僅依靠通過腳手架生成的視圖,但是,當您在磁碟上生成 GSP 時,使它們保持最新的責任就從 Grails 轉移到您自己身上。

如果現在為 Entry 類生成視圖的話,Grails 會提供一個組合框,其中顯示一個 Author 列表,如清單 2 所示。您自己不要 這樣做 — 這只是為了演示。稍後我將提供兩種不同的選項。


清單 2. 為 1:M 關係生成的組合框
				  <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。在 “GORM - 有趣的名稱,嚴肅的技術” 中可以了解到,列表中顯示的文本來自 User.toString() 方法。該文本通常也是表單提交時作為欄位值發回到伺服器的值。在這裡,optionKey 屬性覆蓋欄位值,從而發回 Author 的 id。(要了解更多關於 <g:select> 標記的信息,請參閱 參考資料)。

為 EntryController.groovy 提供 author.id 欄位的最快方式是將一個隱藏欄位添加到表單中,如清單 3 所示。由於執行 create 動作前必須登錄,而登錄的 User 是博客條目的 author,因此對於這個值可以安全地使用 session.user.id。


清單 3. 從表單傳遞 author.id 欄位
				  <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 保存在伺服器上
				  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 閉包,以處理文件上傳,所以您仍可能在安全性方面犯錯誤,將 清單 4 中的代碼添加到 EntryController.groovy 中。重新啟動 Grails,確保可以通過 HTML 表單成功地添加新的 Entry。





文件上傳

現在又可以創建 Entry,接下來該添加另一個特性。我希望用戶在創建新的 Entry 時可以上傳文件。這種文件可以是包含整個博客條目的 HTML,也可以是圖像或任何其他文件。為實現該特性,需要涉及到 Entry domain 類、EntryController 和 GSP 視圖 — 並且要增加一個新的 TagLib。

首先,看看 grails-app/views/entry/create.gsp。添加一個新欄位,用於上傳文件,如清單 5 所示:


清單 5. 添加一個用於文件上傳的欄位
				  <g:uploadForm action="save" method="post" >    <!-- SNIP -->    <tr class="prop">      <td valign="top" class="name">        <label for="payload">File:</label>      </td>      <td valign="top">        <input type="file" id="payload" name="payload"/>      </td>    </tr>  </g:uploadForm>  

注意,<g:form> 標記已經被改為 <g:uploadForm>。這樣便支持從 HTML 表單上傳文件。實際上,也可以保留 <g:form> 標記,並增加一個 enctype="multipart/form-data" 屬性。(用於 HTML 表單的默認 enctype 是 application/x-www-form-urlencoded)。

如果正確設置了表單的 enctype(或者使用 <g:uploadForm>),就可以添加 <input type="file" /> 欄位。這樣便為用戶提供了一個按鈕,用於瀏覽本地文件系統,並選擇上傳的文件,如圖 1 所示。我的例子使用 Grails 徽標;您也可以使用任何自己喜歡的圖像。


圖 1. 包含文件上傳欄位的 Create Entry 表單

現在,客戶端表單已經做好了,接下來可以調整伺服器端代碼,以便用上傳的文件做有用的事情。在文本編輯器中打開 grails-app/controllers/EntryController.groovy,將清單 6 中的代碼添加到 save 閉包中:


清單 6. 顯示關於上傳的文件的信息
				  def save = {      def entryInstance = new Entry(params)      entryInstance.author = User.get(session.user.id)        //handle uploaded file      def uploadedFile = request.getFile('payload')      if(!uploadedFile.empty){        println "Class: ${uploadedFile.class}"        println "Name: ${uploadedFile.name}"        println "OriginalFileName: ${uploadedFile.originalFilename}"        println "Size: ${uploadedFile.size}"        println "ContentType: ${uploadedFile.contentType}"      }        if(!entryInstance.hasErrors() && entryInstance.save()) {          flash.message = "Entry ${entryInstance.id} created"          redirect(action:show,id:entryInstance.id)      }      else {          render(view:'create',model:[entryInstance:entryInstance])      }  }  





[火星人 via ] 精通 Grails: 文件上傳和 Atom 聯合已經有202次圍觀

http://www.coctec.com/docs/linux/show-post-68831.html