javascript
jqGrid,REST,AJAX和Spring MVC集成
兩年多以前,我寫了一篇關(guān)于兩個如何在Struts2中實現(xiàn)優(yōu)雅的CRUD的文章。 實際上,我必須就該主題寫兩篇文章,因為該主題如此廣泛。 今天,我采用了一套更為流行的,完善的框架和庫,采用了更為輕量級的現(xiàn)代方法。 即,我們將在后端使用Spring MVC為資源提供REST接口,為jQuery提供出色的jqGrid插件以呈現(xiàn)表格網(wǎng)格(還有更多!),并且將使用少量JavaScript和AJAX進行所有連接。
后端實際上是該展示柜中最不有趣的部分,它可以使用能夠處理RESTful請求的任何服務(wù)器端技術(shù)來實現(xiàn),現(xiàn)在應(yīng)該將JAX-RS視為該領(lǐng)域的標準。 我沒有任何理由就選擇了Spring MVC,但這對于這個任務(wù)來說也不是一個壞選擇。 我們將通過REST接口公開CRUD操作; 歷史上最暢銷的書籍清單將是我們的領(lǐng)域模型(您能猜出誰登上領(lǐng)獎臺嗎?)
@Controller @RequestMapping(value = "/book") public class BookController {private final Map<Integer, Book> books = new ConcurrentSkipListMap<Integer, Book>();@RequestMapping(value = "/{id}", method = GET)public @ResponseBody Book read(@PathVariable("id") int id) {return books.get(id);}@RequestMapping(method = GET)public @ResponseBody Page<Book> listBooks(@RequestParam(value = "page", required = false, defaultValue = "1") int page,@RequestParam(value = "max", required = false, defaultValue = "20") int max) {final ArrayList<Book> booksList = new ArrayList<Book>(books.values());final int startIdx = (page - 1) * max;final int endIdx = Math.min(startIdx + max, books.size());return new Page<Book>(booksList.subList(startIdx, endIdx), page, max, books.size());}}幾乎沒有什么需要解釋的。 首先,出于這個簡單展示的目的,我沒有使用任何數(shù)據(jù)庫,所有書籍都存儲在控制器內(nèi)部的內(nèi)存映射中。 原諒我。 第二個問題更加微妙。 由于在如何使用RESTful Web服務(wù)處理分頁方面似乎尚未達成共識 ,因此我使用了簡單的查詢參數(shù)。 您可能會發(fā)現(xiàn)它很丑陋,但是我發(fā)現(xiàn)濫用Accept-Ranges和Range標頭以及206 HTTP響應(yīng)代碼更加丑陋。
最后一個值得注意的細節(jié)是Page wrapper類:
@XmlRootElement public class Page<T> {private List<T> rows;private int page;private int max;private int total;//...}我可以返回原始列表(或更確切地說,返回列表的請求部分),但是我還需要一種方法來向視圖層提供方便的元數(shù)據(jù)(如記錄總數(shù)),更不用說在編組/解組原始列表時遇到的一些困難。
現(xiàn)在,我們準備啟動我們的應(yīng)用程序并使用curl進行一些測試:
<!-- $ curl -v "http://localhost:8080/books/rest/book?page=1&max=2" --><?xml version="1.0" encoding="UTF-8" standalone="yes"?> <page><total>43</total><page>1</page><max>3</max><rows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="book"><author>Charles Dickens</author><available>true</available><cover>PAPERBACK</cover><id>1</id><publishedYear>1859</publishedYear><title>A Tale of Two Cities</title></rows><rows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="book"><author>J. R. R. Tolkien</author><available>true</available><cover>HARDCOVER</cover><id>2</id><publishedYear>1954</publishedYear><title>The Lord of the Rings</title></rows><rows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="book"><author>J. R. R. Tolkien</author><available>true</available><cover>PAPERBACK</cover><id>3</id><publishedYear>1937</publishedYear><title>The Hobbit</title></rows> </page>如果未指定,則響應(yīng)類型默認為XML,但是如果我們將Jackson庫添加到CLASSPATH,Spring會選擇它并允許我們使用JSON:
// $ curl -v -H "Accept: application/json" "http://localhost:8080/books/rest/book?page=1&max=3"{"total":43,"max":3,"page":1,"rows":[{"id":1,"available":true,"author":"Charles Dickens","title":"A Tale of Two Cities","publishedYear":1859,"cover":"PAPERBACK","comments":null},{"id":2,"available":true,"author":"J. R. R. Tolkien","title":"The Lord of the Rings","publishedYear":1954,"cover":"HARDCOVER","comments":null},{"id":3,"available":true,"author":"J. R. R. Tolkien","title":"The Hobbit","publishedYear":1937,"cover":"PAPERBACK","comments":null}] }很好,現(xiàn)在我們可以在前端工作了,希望不會使我們的手太臟。 關(guān)于HTML標記,這就是我們所需要的,認真的是:
<table id="grid"></table> <div id="pager"></div>請記住,我們將實現(xiàn)所有CRUD操作,但這仍然是我們所需要的。 沒有更多HTML。 多虧了出色的jqGrid庫,其余的魔術(shù)才得以實現(xiàn)。 這是一個基本設(shè)置:
$("#grid").jqGrid({url:'rest/book',colModel:[{name:'id', label: 'ID', formatter:'integer', width: 40},{name:'title', label: 'Title', width: 300},{name:'author', label: 'Author', width: 200},{name:'publishedYear', label: 'Published year', width: 80, align: 'center'},{name:'available', label: 'Available', formatter: 'checkbox', width: 46, align: 'center'}],caption: "Books",pager : '#pager',height: 'auto'}).navGrid('#pager', {edit:false,add:false,del:false, search: false});從技術(shù)上講,這就是我們所需要的。 用來獲取數(shù)據(jù)的URL,指向我們的控制器(jqGrid將為我們執(zhí)行所有AJAX魔術(shù))和數(shù)據(jù)模型(您可能會識別書本字段及其描述)。 但是,由于jqGrid是高度可定制的,因此我進行了一些調(diào)整以使網(wǎng)格看起來更好。 另外,我也不喜歡建議使用的元數(shù)據(jù)名稱,例如從服務(wù)器返回的字段總數(shù)應(yīng)該是頁面總數(shù),而不是記錄總數(shù),這很違反直覺。 這是我調(diào)整的選項:
$.extend($.jgrid.defaults, {datatype: 'json',jsonReader : {repeatitems:false,total: function(result) {//Total number of pagesreturn Math.ceil(result.total / result.max);},records: function(result) {//Total number of recordsreturn result.total;}},prmNames: {rows: 'max', search: null},height: 'auto',viewrecords: true,rowList: [10, 20, 50, 100],altRows: true,loadError: function(xhr, status, error) {alert(error);}});渴望看到結(jié)果嗎? 這是瀏覽器的屏幕截圖:
漂亮的外觀,可自定義的分頁,輕巧的刷新……而且我們的手還是比較干凈的! 但是我答應(yīng)了CRUD ...如果您小心的話,您可能已經(jīng)注意到了一些navGrid屬性,并且很想打開它:
var URL = 'rest/book'; var options = {url: URL,editurl: URL,colModel:[{name:'id', label: 'ID',formatter:'integer',width: 40,editable: true,editoptions: {disabled: true, size:5}},{name:'title',label: 'Title',width: 300,editable: true,editrules: {required: true}},{name:'author',label: 'Author',width: 200,editable: true,editrules: {required: true}},{name:'cover',label: 'Cover',hidden: true,editable: true,edittype: 'select',editrules: {edithidden:true},editoptions: {value: {'PAPERBACK': 'paperback', 'HARDCOVER': 'hardcover', 'DUST_JACKET': 'dust jacket'}}},{name:'publishedYear',label: 'Published year',width: 80,align: 'center',editable: true,editrules: {required: true, integer: true},editoptions: {size:5, maxlength: 4}},{name:'available',label: 'Available',formatter: 'checkbox',width: 46,align: 'center',editable: true,edittype: 'checkbox',editoptions: {value:"true:false"}},{name:'comments',label: 'Comments',hidden: true,editable: true,edittype: 'textarea',editrules: {edithidden:true}}],caption: "Books",pager : '#pager',height: 'auto' }; $("#grid").jqGrid(options).navGrid('#pager', {edit:true,add:true,del:true, search: false});配置變得非常冗長,但并沒有什么復(fù)雜的事情–對于每個字段,我們都添加了一些其他屬性來控制在編輯模式下應(yīng)如何處理該字段。 這包括應(yīng)該代表哪種類型HTML輸入,驗證規(guī)則,可見性等。但是說實話,我認為這是值得的:
jqGrid根據(jù)上面提到的我們的編輯選項(包括驗證邏輯)完全生成了一個外觀漂亮的編輯窗口。 我們可以在編輯對話框中使某些字段在網(wǎng)格中隱藏/不活動(如id)可見,反之亦然(封面和注釋不存在于網(wǎng)格中,但是您可以對其進行修改)。 另外請注意,在網(wǎng)格的左下角幾乎看不到新圖標。 添加和刪??除也是可能的-我們還沒有編寫一行HTML / JSP / JavaScript(jqGrid配置對象除外)。
當然,我們都知道用戶界面就是應(yīng)用程序 ,我們的界面還不錯,但是有時候我們真的想要一個漂亮且可以運行的應(yīng)用程序。 當前,后一個要求是我們的致命弱點。 不是因為后端尚未準備好,而是相當簡單的:
@Controller @RequestMapping(value = "/book") public class BookController {private final Map<Integer, Book> books = new ConcurrentSkipListMap<Integer, Book>();@RequestMapping(value = "/{id}", method = GET)public @ResponseBody Book read(@PathVariable("id") int id) {//...}@RequestMapping(method = GET)public@ResponseBodyPage<Book> listBooks(@RequestParam(value = "page", required = false, defaultValue = "1") int page,@RequestParam(value = "max", required = false, defaultValue = "20") int max) {//...}@RequestMapping(value = "/{id}", method = PUT)@ResponseStatus(HttpStatus.NO_CONTENT)public void updateBook(@PathVariable("id") int id, @RequestBody Book book) {//...}@RequestMapping(method = POST)public ResponseEntity<String> createBook(HttpServletRequest request, @RequestBody Book book) {//...}@RequestMapping(value = "/{id}", method = DELETE)@ResponseStatus(HttpStatus.NO_CONTENT)public void deleteBook(@PathVariable("id") int id) {//...}}服務(wù)器端已經(jīng)準備就緒,但是當涉及到客戶端的數(shù)據(jù)操作時,jqGrid會揭示其骯臟的秘密–到服務(wù)器的所有流量都使用POST發(fā)送,如下所示:
Content-Type: application/x-www-form-urlencoded in the following format: id=&title=And+Then+There+Were+None&author=Agatha+Christie&cover=PAPERBACK&publishedYear=1939&available=true&comments=&oper=add最后一個屬性(oper = add)是至關(guān)重要的。 不是真正的慣用REST,您不覺得嗎? 如果我們只能適當?shù)厥褂肞OST / PUT / DELETE并使用JSON或XML序列化數(shù)據(jù)……修改服務(wù)器,使其與某些JavaScript庫兼容(無論它有多酷),那似乎是不得已的方法。 值得慶幸的是,只需少量的工作就可以自定義所有內(nèi)容。
$.extend($.jgrid.edit, {ajaxEditOptions: { contentType: "application/json" },mtype: 'PUT',serializeEditData: function(data) {delete data.oper;return JSON.stringify(data);}}); $.extend($.jgrid.del, {mtype: 'DELETE',serializeDelData: function() {return "";}});var URL = 'rest/book'; var options = {url: URL,//... }var editOptions = {onclickSubmit: function(params, postdata) {params.url = URL + '/' + postdata.id;} }; var addOptions = {mtype: "POST"}; var delOptions = {onclickSubmit: function(params, postdata) {params.url = URL + '/' + postdata;} };$("#grid").jqGrid(options).navGrid('#pager',{}, //optionseditOptions,addOptions,delOptions,{} // search options );我們?yōu)槊總€操作定制了HTTP方法,使用JSON處理序列化,最后用于編輯和刪除操作的URL現(xiàn)在帶有/ record_id后綴。 現(xiàn)在它不僅看起來,而且可以工作! 查看瀏覽器與服務(wù)器的交互(注意不同的HTTP方法和URL):
這是在瀏覽器端創(chuàng)建新資源的示例:
為了盡可能嚴格地遵循REST原則,我返回201 Created響應(yīng)代碼以及Location標頭,該標頭指向新創(chuàng)建的資源。 如您所見,數(shù)據(jù)現(xiàn)在以JSON格式發(fā)送到服務(wù)器。
總而言之,這種方法具有很多優(yōu)點:
- GUI非常敏感,頁面會立即顯示(它可以是CDN提供的靜態(tài)資源),而數(shù)據(jù)是通過AJAX以輕量級JSON格式異步加載的
- 我們免費獲得CRUD操作
- 其他系統(tǒng)的REST接口也是免費的
將其與任何Web框架進行比較。 我是否在JavaScript結(jié)霜方面提到了這個小問題:jqGrid完全符合jQuery UI主題 ,還支持國際化。 這是具有更改的主題和語言的同一應(yīng)用程序:
完整的源代碼可在Tomek的GitHub帳戶上獲得 。 該應(yīng)用程序是獨立的,只需構(gòu)建它并將其部署到某個servlet容器中即可。
參考: 窮人的CRUD:jQGrid,REST,AJAX和Spring MVC在我們的JCG合作伙伴 Tomek Nurkiewicz的NoBlogDefFound博客中合而為一 。
相關(guān)文章 :- Spring3 RESTful Web服務(wù)
- Tomcat 7上具有RESTeasy JAX-RS的RESTful Web服務(wù)-Eclipse和Maven項目
- Spring MVC3 Hibernate CRUD示例應(yīng)用程序
- Spring MVC開發(fā)–快速教程
- 帶有Spring和Maven教程的JAX–WS
翻譯自: https://www.javacodegeeks.com/2011/07/jqgrid-rest-ajax-spring-mvc-integration.html
總結(jié)
以上是生活随笔為你收集整理的jqGrid,REST,AJAX和Spring MVC集成的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在u盘安装linux系统(在u盘安装li
- 下一篇: 安卓对战平台(安卓对战)