jqGrid,REST,AJAX和Spring MVC集成

兩年多以前,我寫了一篇關于兩個如何在Struts2中實現優雅的CRUD的文章。 實際上,我必須就該主題寫兩篇文章,因為該主題如此廣泛。 今天,我采用了一套更為流行的,完善的框架和庫,采用了更為輕量級的現代方法。 即,我們將在后端使用Spring MVC為資源提供REST接口,為jQuery提供出色的jqGrid插件以呈現表格網格(還有更多!),并且將使用少量JavaScript和AJAX進行所有連接。

后端實際上是該展示柜中最不有趣的部分,它可以使用能夠處理RESTful請求的任何服務器端技術來實現,現在應該將JAX-RS視為該領域的標準。 我沒有任何理由就選擇了Spring MVC,但這對于這個任務來說也不是一個壞選擇。 我們將通過REST接口公開CRUD操作; 歷史上最暢銷的書籍清單將是我們的領域模型(您能猜出誰登上領獎臺嗎?)

@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());}}

幾乎沒有什么需要解釋的。 首先,出于這個簡單展示的目的,我沒有使用任何數據庫,所有書籍都存儲在控制器內部的內存映射中。 原諒我。 第二個問題更加微妙。 由于在如何使用RESTful Web服務處理分頁方面似乎尚未達成共識 ,因此我使用了簡單的查詢參數。 您可能會發現它很丑陋,但是我發現濫用Accept-Ranges和Range標頭以及206 HTTP響應代碼更加丑陋。

最后一個值得注意的細節是Page wrapper類:

@XmlRootElement
public class Page<T> {private List<T> rows;private int page;private int max;private int total;//...}

我可以返回原始列表(或更確切地說,返回列表的請求部分),但是我還需要一種方法來向視圖層提供方便的元數據(如記錄總數),更不用說在編組/解組原始列表時遇到的一些困難。

現在,我們準備啟動我們的應用程序并使用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>

如果未指定,則響應類型默認為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}]
}

很好,現在我們可以在前端工作了,希望不會使我們的手太臟。 關于HTML標記,這就是我們所需要的,認真的是:

<table id="grid"></table>
<div id="pager"></div>

請記住,我們將實現所有CRUD操作,但這仍然是我們所需要的。 沒有更多HTML。 多虧了出色的jqGrid庫,其余的魔術才得以實現。 這是一個基本設置:

$("#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});

從技術上講,這就是我們所需要的。 用來獲取數據的URL,指向我們的控制器(jqGrid將為我們執行所有AJAX魔術)和數據模型(您可能會識別書本字段及其描述)。 但是,由于jqGrid是高度可定制的,因此我進行了一些調整以使網格看起來更好。 另外,我也不喜歡建議使用的元數據名稱,例如從服務器返回的字段總數應該是頁面總數,而不是記錄總數,這很違反直覺。 這是我調整的選項:

$.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);}});

渴望看到結果嗎? 這是瀏覽器的屏幕截圖:

漂亮的外觀,可自定義的分頁,輕巧的刷新……而且我們的手還是比較干凈的! 但是我答應了CRUD ...如果您小心的話,您可能已經注意到了一些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});

配置變得非常冗長,但并沒有什么復雜的事情–對于每個字段,我們都添加了一些其他屬性來控制在編輯模式下應如何處理該字段。 這包括應該代表哪種類型HTML輸入,驗證規則,可見性等。但是說實話,我認為這是值得的:

jqGrid根據上面提到的我們的編輯選項(包括驗證邏輯)完全生成了一個外觀漂亮的編輯窗口。 我們可以在編輯對話框中使某些字段在網格中隱藏/不活動(如id)可見,反之亦然(封面和注釋不存在于網格中,但是您可以對其進行修改)。 另外請注意,在網格的左下角幾乎看不到新圖標。 添加和刪??除也是可能的-我們還沒有編寫一行HTML / JSP / JavaScript(jqGrid配置對象除外)。

當然,我們都知道用戶界面就是應用程序 ,我們的界面還不錯,但是有時候我們真的想要一個漂亮且可以運行的應用程序。 當前,后一個要求是我們的致命弱點。 不是因為后端尚未準備好,而是相當簡單的:

@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) {//...}}

服務器端已經準備就緒,但是當涉及到客戶端的數據操作時,jqGrid會揭示其骯臟的秘密–到服務器的所有流量都使用POST發送,如下所示:

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)是至關重要的。 不是真正的慣用REST,您不覺得嗎? 如果我們只能適當地使用POST / PUT / DELETE并使用JSON或XML序列化數據……修改服務器,使其與某些JavaScript庫兼容(無論它有多酷),那似乎是不得已的方法。 值得慶幸的是,只需少量的工作就可以自定義所有內容。

$.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
);

我們為每個操作定制了HTTP方法,使用JSON處理序列化,最后用于編輯和刪除操作的URL現在帶有/ record_id后綴。 現在它不僅看起來,而且可以工作! 查看瀏覽器與服務器的交互(注意不同的HTTP方法和URL):

這是在瀏覽器端創建新資源的示例:

為了盡可能嚴格地遵循REST原則,我返回201 Created響應代碼以及Location標頭,該標頭指向新創建的資源。 如您所見,數據現在以JSON格式發送到服務器。

總而言之,這種方法具有很多優點:

  • GUI非常敏感,頁面會立即顯示(它可以是CDN提供的靜態資源),而數據是通過AJAX以輕量級JSON格式異步加載的
  • 我們免費獲得CRUD操作
  • 其他系統的REST接口也是免費的

將其與任何Web框架進行比較。 我是否在JavaScript結霜方面提到了這個小問題:jqGrid完全符合jQuery UI主題 ,還支持國際化。 這是具有更改的主題和語言的同一應用程序:

完整的源代碼可在Tomek的GitHub帳戶上獲得 。 該應用程序是獨立的,只需構建它并將其部署到某個servlet容器中即可。

參考: 窮人的CRUD:jQGrid,REST,AJAX和Spring MVC在我們的JCG合作伙伴 Tomek Nurkiewicz的NoBlogDefFound博客中合而為一 。

相關文章 :
  • Spring3 RESTful Web服務
  • Tomcat 7上具有RESTeasy JAX-RS的RESTful Web服務-Eclipse和Maven項目
  • Spring MVC3 Hibernate CRUD示例應用程序
  • Spring MVC開發–快速教程
  • 帶有Spring和Maven教程的JAX–WS

翻譯自: https://www.javacodegeeks.com/2011/07/jqgrid-rest-ajax-spring-mvc-integration.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374933.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374933.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374933.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java-馬士兵設計模式學習筆記-代理模式--動態代理 修改成可以代理任意接口

一、概述 1.目標&#xff1a;把Proxy修改成可以代理任意接口及其任意方法 2.思路&#xff1a; (1)代理任意接口&#xff1a;把接口類型作為參數傳給Proxy的newProxyInstance(Class interfze) (2)代理任意方法&#xff1a;用interfze.getMethods()取出所有方法&#xff0c;拼接實…

PTA習題

PTA習題 PTA浙大版《C語言程序設計&#xff08;第3版&#xff09;》 題目集5-6 水仙花數是指一個N位正整數&#xff08;N≥3&#xff09;&#xff0c;它的每個位上的數字的N次冪之和等于它本身。 本題要求編寫兩個函數&#xff0c;一個判斷給定整數是否水仙花數&#xff0c;…

acdream 1409 Musical 狀壓DP

鏈接&#xff1a;http://acdream.info/problem?pid1409 題意&#xff1a;整個國家有n座城市&#xff0c;每座城市有三種粉絲。 第一種一周看一場音樂劇&#xff0c;挑選的音樂劇是已經在周圍城市播放上演過的次數最多的音樂劇中的隨機一個。 另外一種每天看一場音樂劇&#xf…

真正的模塊化Web應用程序:為什么沒有開發標準?

OSGI &#xff0c; SpringSource &#xff0c; Jboss模塊 &#xff0c;J2EE和清單永遠不會結束。所有這些技術都向他們的最終用戶/開發人員保證了相同的東西&#xff0c;或多或少是Java模塊化Web應用程序&#xff08;&#xff1f;&#xff09;。 但是&#xff0c;我們當中有多少…

C語言5-7習題

本題要求實現一個函數&#xff0c;用下列公式求cos(x)的近似值&#xff0c;精確到最后一項的絕對值小于e&#xff1a; #include <stdio.h> #include <math.h>double funcos( double e, double x );int main() { double e, x;scanf("%lf %lf", &…

JDBC批處理executeBatch

JDBC運行SQL聲明&#xff0c;有兩個處理接口&#xff0c;一PreparedStatement,Statement,一般程序JDBC有多少仍然比較PreparedStatement 只要運行批處理&#xff0c;PreparedStatement少一點Statement ps conn.prepareStatement(sql); for(int i 0;i<10;i){ ps.setString(…

BC div2補題以及 復習模除 逆元__BestCoder Round #78 (div.2)

第一題沒話說 智商欠費 加老柴輔導終于過了 需要在意的是數據范圍為2的63次方-1 三個數相加肯定爆了 四邊形的定義  任意邊小于其余三邊之和 換句話說就是  最長邊小于其余三邊之和 這樣的話問題轉化為 最長邊依次減其余三邊的結果是否小于等于0 還有一點是題目出現0邊 即最…

習題6-1 分類統計字符個數 (15 分)

本題要求實現一個函數&#xff0c;統計給定字符串中英文字母、空格或回車、數字字符和其他字符的個數。 函數接口定義&#xff1a; void StringCount( char s[] );其中 char s[] 是用戶傳入的字符串。函數StringCount須在一行內按照 letter 英文字母個數, blank 空格或回車…

Servlet 3.0異步處理可將服務器吞吐量提高十倍

Servlet是Java中處理服務器端邏輯的主要組件&#xff0c;新的3.0規范引入了一些非常有趣的功能&#xff0c;其中異步處理是最重要的功能之一。 可以利用異步處理來開發高度可伸縮的Web應用程序。 使用此功能可以有效地構建Web 2.0站點和AJAX應用程序。 我們的JCG合作伙伴之一To…

使用secureCRT連接VMware-Ubuntukylin虛擬機

使用SecureCRT連接VMware時總是提醒主機拒絕連接。這時可以使用sudo apt-get install openssh-server openssh-client&#xff0c;在主機上安裝ssh. 安裝成功后&#xff0c;可以連接到主機了。 如果顯示遠程主機拒絕連接。則可以使用如下方法。 VMware里面裝的是Ubuntukylin版本…

加載音頻Audio

var cameraAudio new Audio(); cameraAudio.src camera.wav;// 設置音頻對象的屬性,預加載視頻 var options_audio { preload : auto } for(var key in options_audio){ if(options_audio.hasOwnProperty(key) && (key in cameraAudio)){ cameraAudio[key] opti…

習題6-2 使用函數求特殊a串數列和 (20 分)

給定兩個均不超過9的正整數a和n&#xff0c;要求編寫函數求aaaaaa?aa?a&#xff08;n個a&#xff09;之和。 int fn( int a, int n ); int SumA( int a, int n );其中函數fn須返回的是n個a組成的數字&#xff1b;SumA返回要求的和。 我的代碼&#xff1a; int fn( int a, i…

Java中可怕的雙重檢查鎖定成語

本文討論的問題不是新問題&#xff0c;但即使是經驗豐富的開發人員也仍然很棘手。 單例模式是常見的編程習慣。 但是&#xff0c;當與多個線程一起使用時&#xff0c;必須進行某種類型的同步&#xff0c;以免破壞代碼。 Khangaonkar報告中的 JCG合作伙伴Manoj Khangaonkar在一篇…

國內有哪些好的刷題網站?

http://www.zhihu.com/question/25574458 Luau Lawrence&#xff0c;Data Mining 弱雞 / PhDNTU 溫夢強、石一帆、知乎用戶 等人贊同 - Welcome To PKU JudgeOnline 北京大學的Online Judge。POJ上面的題目有點老了&#xff0c;但好處是做的人多&#xff0c;經典算法題多&…

IE版本判斷

我們常常會在網頁的HTML里面看到形如[if lte IE 9]……[endif]的代碼&#xff0c;表示的是限定某些瀏覽器版本才能執行的語句&#xff0c;那么這些判斷語句的規則是什么呢&#xff1f;請看下文&#xff1a; <!--[if !IE]><!--> 除IE外都可識別 <!--<![endif]…

Js 流程控制

流程控制 順序、分支、循環 順序結構 代碼一行一行從上往下執行并解析 分支結構 if語句 switch語句 if語句 單分支 if(條件表達式){ //語句塊 } 含義&#xff1a;當條件表達式為真的時候就執行里面的語句塊 示例&#xff1a; 雙分支&#xff1a; if(條件表達式){ //語句塊1 }el…

習題6-3 使用函數輸出指定范圍內的完數 (20 分)

本題要求實現一個計算整數因子和的簡單函數&#xff0c;并利用其實現另一個函數&#xff0c;輸出兩正整數m和n&#xff08;0<m≤n≤10000&#xff09;之間的所有完數。所謂完數就是該數恰好等于除自身外的因子之和。例如&#xff1a;6123&#xff0c;其中1、2、3為6的因子。…

速覽Java 7 MethodHandle及其用法

由于Java的Reflection API&#xff0c;我們已經能夠在運行時檢查和更改程序執行。 特別是&#xff0c;我們可以在運行時觀察接口/類/方法和字段&#xff0c;而在編譯時不知道它們的名稱。 JDK 7為這種動態/運行時檢查引入了一個新的參與者&#xff0c;即方法句柄&#xff08;即…

習題6-4 使用函數輸出指定范圍內的Fibonacci數 (20 分)

本題要求實現一個計算Fibonacci數的簡單函數&#xff0c;并利用其實現另一個函數&#xff0c;輸出兩正整數m和n&#xff08;0<m≤n≤10000&#xff09;之間的所有Fibonacci數。所謂Fibonacci數列就是滿足任一項數字是前兩項的和&#xff08;最開始兩項均定義為1&#xff09;…

SmartGWT入門,提供出色的GWT界面

SmartGWT簡介 我最近開始使用SmartGWT &#xff0c;它是一個基于GWT的框架&#xff0c;該框架為您的應用程序UI提供了一個全面的小部件庫&#xff0c;并為服務器端的數據管理提供了幫助。 您可以在SmartGWT展示柜上查看其漂亮的功能。 我準備了一個簡短的“入門”指南&#xf…