很多時候前端都需要調用后台服務實現交互功能,常見的數據交換格式多是JSON或XML,這里主要講解Spring MVC為前端提供JSON格式的數據並實現與前台交互。RESTful則是一種軟件架構風格、設計風格,而不是標准,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。
一、JSON
1.1、概要
JSON(JavaScript Object Notation, JS 對象標記) 是一種輕量級的數據交換格式。它基於 ECMAScript (w3c制定的js規范)的一個子集,采用完全獨立於編程語言的文本格式來存儲和表示數據。簡潔和清晰的層次結構使得 JSON 成為理想的數據交換語言。 易於人閱讀和編寫,同時也易於機器解析和生成,並有效地提升網絡傳輸效率。
在 JS 語言中,一切都是對象。因此,任何支持的類型都可以通過 JSON 來表示,例如字符串、數字、對象、數組等。但是對象和數組是比較特殊且常用的兩種類型。
要實現從對象轉換為 JSON 字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'}); //結果是 '{"a": "Hello", "b": "World"}'
要實現從 JSON 轉換為對象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //結果是 {a: 'Hello', b: 'World'}
示例:

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <!--[if lte IE 8]> <script type="text/javascript" src="//res.wx.qq.com/a/wx_fed/webwx/res/json3.min.js"></script> <![endif]--> </head> <body> <script type="text/javascript"> //js對象 var user = { "name": "張學友", "address": "中國香港" }; //將對象轉換成字符 var str = JSON.stringify(user); alert(str); //將字符串轉換成json對象 var zxy = JSON.parse(str); alert(zxy.name + "," + zxy.address); </script> </body> </html>
結果:
1.2、使用ModelAndView
修改pom.xml添加對jackson的依賴
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.2</version> </dependency>
在user控制器中添加一個action
@RequestMapping(value = "/users") public ModelAndView users(){ ModelAndView mav=new ModelAndView(new MappingJackson2JsonView()); mav.addObject(userService.queryAllUsers()); return mav; }
運行結果:
1.3、使用@ResponseBody與Jackson
修改pom.xml添加對jackson的依賴
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.2</version> </dependency>
添加一個action,使用注解@ResponseBody,響應主體而不是路徑
@RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8") @ResponseBody public String userJson(){ ObjectMapper mapper=new ObjectMapper(); try { return mapper.writeValueAsString(userService.queryAllUsers()); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; }
結果:
1.4、亂碼問題
1.4.1、方法一在action上聲明編碼格式
@RequestMapping(path="/json",produces = "application/json;charset=UTF-8")
1.4.2、方法二修改Spring配置文件
上一種方法比較麻煩,如果項目中有許多action則每一個都要添加,可以通過Spring配置統一指定
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
1.5、日期格式化問題
默認日期格式會變成一個數字,是1970年1月1日到當前日期的毫秒數:
Jackson 默認是轉成timestamps形式
1.5.1、方法一注解字段
在實體字段上使用@JsonFormat注解格式化日期
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
代碼:
/** * 出生日期 */ @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss") private Date birthday;
結果:
1.5.2、方法二取消timestamps形式
如果只取消則會得到一個默認的日期格式,效果如下:
當然自定義輸出格式是允許的
@RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8") @ResponseBody public String userJson(){ ObjectMapper mapper=new ObjectMapper(); //不使用時間差的方式 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //自定義日期格式對象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //指定日期格式 mapper.setDateFormat(sdf); try { return mapper.writeValueAsString(userService.queryAllUsers()); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; }
運行結果:
1.6、工具類
工具類可以復用代碼,提高開發效率,如上文中的序列化JSON:
package com.zhangguo.springmvc08.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import java.text.SimpleDateFormat; /** * JSON工具類,輔助類 * */ public class JsonUtil { public static String getJson(Object object) { return getJson(object,"yyyy-MM-dd HH:mm:ss"); } public static String getJson(Object object,String dateFormat) { ObjectMapper mapper = new ObjectMapper(); //不使用時間差的方式 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //自定義日期格式對象 SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); //指定日期格式 mapper.setDateFormat(sdf); try { return mapper.writeValueAsString(object); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } }
調用:
@RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8") @ResponseBody public String userJson(){ return JsonUtil.getJson(userService.queryAllUsers(),"yyyy-MM-dd"); }
如對MySQL數據庫的訪問封裝:

package com.zhangguo.springmvc08.utils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class JDBCUtil { public static String DRIVER="com.mysql.jdbc.Driver"; public static String URL="jdbc:mysql://127.0.0.1:3306/schoolmis?useUnicode=true&characterEncoding=UTF-8"; public static String USER_NAME="root"; public static String PASSWORD="pwd"; //加載驅動 static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } private JDBCUtil() { } /** * 獲得連接 * * @return */ public static Connection getconnnection() { Connection con = null; try { con = DriverManager.getConnection(URL, USER_NAME, PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return con; } /** * 關閉連接 * * @param rs * @param st * @param con */ public static void close(ResultSet rs, Statement st, Connection con) { try { try { if (rs != null) { rs.close(); } } finally { try { if (st != null) { st.close(); } } finally { if (con != null) con.close(); } } } catch (SQLException e) { e.printStackTrace(); } } /** * 關閉連接 * * @param rs */ public static void close(ResultSet rs) { Statement st = null; Connection con = null; try { try { if (rs != null) { st = rs.getStatement(); rs.close(); } } finally { try { if (st != null) { con = st.getConnection(); st.close(); } } finally { if (con != null) { con.close(); } } } } catch (SQLException e) { e.printStackTrace(); } } /** * 關閉連接 * * @param st * @param con */ public static void close(Statement st, Connection con) { try { try { if (st != null) { st.close(); } } finally { if (con != null) con.close(); } } catch (SQLException e) { e.printStackTrace(); } } /** * insert/update/delete * 增加/更新/刪除 * * @param sql 數據庫語句 * @param args 可變參數(可以不帶參數,可以帶0-n個參數) * @return */ public static int update(String sql, Object... args) { int result = 0; Connection con = getconnnection(); PreparedStatement ps = null; try { ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } result = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(ps, con); } return result; } /** * query, because need to manually close the resource, so not recommended * for use it * * @param sql * @param args * @return ResultSet */ @Deprecated //注解 public static ResultSet query(String sql, Object... args) { ResultSet result = null; Connection con = getconnnection(); PreparedStatement ps = null; try { ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } result = ps.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } return result; } /** * Query a single record * 查詢單個記錄 * @param sql * @param args * @return Map<String,Object> */ public static Map<String, Object> queryForMap(String sql, Object... args) { Map<String, Object> result = new HashMap<String, Object>(); List<Map<String, Object>> list = queryForList(sql, args); if (list.size() > 0) { result = list.get(0); } return result; } /** * Query a single record * 查詢單個記錄返回強類型對象 * @param sql * @param args * @return <T> //泛型 */ public static <T> T queryForObject(String sql, Class<T> clz, Object... args) { T result = null; List<T> list = queryForList(sql, clz, args); if (list.size() > 0) { result = list.get(0); } return result; } /** * Query a single record * * @param sql * @param args * @return List<Map<String,Object>> */ public static List<Map<String, Object>> queryForList(String sql, Object... args) { List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); Connection con = null; ResultSet rs = null; PreparedStatement ps = null; try { con = getconnnection(); ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); while (rs.next()) { Map<String, Object> map = new HashMap<String, Object>(); for (int i = 1; i <= columnCount; i++) { map.put(rsmd.getColumnLabel(i), rs.getObject(i)); } result.add(map); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, ps, con); } return result; } /** * Query records * 查詢多個對象,返回強類型集合 * @param sql * @param args * @return List<T> */ public static <T> List<T> queryForList(String sql, Class<T> clz, Object... args) { List<T> result = new ArrayList<T>(); Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { con = getconnnection(); ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); while (rs.next()) { T obj = clz.newInstance(); for (int i = 1; i <= columnCount; i++) { String columnName = rsmd.getColumnName(i); String methodName = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1, columnName.length()); Method method[] = clz.getMethods(); for (Method meth : method) { if (methodName.equals(meth.getName())) { meth.invoke(obj, rs.getObject(i)); } } } result.add(obj); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } finally { close(rs, ps, con); } return result; } }
1.7、數據持久化
上一章的示例中並沒有直接訪問數據庫,數據以集合的形式存放在內存中,這里使用MySQL將數據存儲到數據庫中。該示例基於第8章的示例,請先熟悉第8章的內容《Spring MVC 學習總結(八)——Spring MVC概要與環境配置(IDEA+Maven+Tomcat7+JDK8、示例與視頻)》
1.7.1、創建數據庫與表
開啟MySQL服務
打開管理工具Navicat
創建數據庫
新建表
/* Navicat MySQL Data Transfer Source Server : localhostMe Source Server Version : 50506 Source Host : localhost:3306 Source Database : mvcdb Target Server Type : MYSQL Target Server Version : 50506 File Encoding : 65001 Date: 2017-12-07 14:08:35 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `name` varchar(32) NOT NULL COMMENT '姓名', `birthday` datetime DEFAULT NULL COMMENT '生日', `address` varchar(128) DEFAULT NULL COMMENT '地址', `phone` varchar(11) DEFAULT NULL COMMENT '電話', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ----------------------------
1.7.2、添加測試數據
insert into user(name,birthday,address,phone) select '張學友','1968-09-08','中國香港','18989890098' union select '張惠妹','1969-01-05','中國北京','13345678781' union select '張國立',SYSDATE(),'中國珠海','13567453422' select id,name,birthday,address,phone from user;
1.7.3、添加數據庫驅動修改連接信息
在pom.xml中依賴MySQL驅動包
<!--mysql驅動包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency>
修改工具類的中數據庫連接信息
public static String DRIVER="com.mysql.jdbc.Driver"; public static String URL="jdbc:mysql://127.0.0.1:3306/mvcdb?useUnicode=true&characterEncoding=UTF-8"; public static String USER_NAME="root"; public static String PASSWORD="pwd";
1.7.4、新增UserDAOPro
新增UserDAOPro類,實現MySQL數據庫訪問,代碼如下:
package com.zhangguo.springmvc08.dao; import com.zhangguo.springmvc08.entity.User; import com.zhangguo.springmvc08.utils.JDBCUtil; import org.springframework.stereotype.Repository; import java.util.List; @Repository("mysql") public class UserDAOPro implements IUserDAO { public List<User> getAll() { return JDBCUtil.queryForList("select id,name,birthday,address,phone from user", User.class); } public User getUserById(int id) { return JDBCUtil.queryForObject("select id,name,birthday,address,phone from user where id=?", User.class, id); } public boolean add(User user) { return JDBCUtil.update("insert into user(name,birthday,address,phone) values(?,?,?,?)", user.getName(), user.getBirthday(), user.getAddress(), user.getPhone()) > 0; } public boolean delete(int id) { return JDBCUtil.update("delete from user where id=?", id) > 0; } public boolean update(User user) { return JDBCUtil.update("update user set name=?,birthday=?,address=?,phone=? where id=?", user.getName(), user.getBirthday(), user.getAddress(), user.getPhone(), user.getId()) > 0; } }
1.7.5、修改用戶業務類
因為系統中有兩個類實現了IUserDAO,指定名稱:
package com.zhangguo.springmvc08.service; import com.zhangguo.springmvc08.dao.IUserDAO; import com.zhangguo.springmvc08.dao.UserDAO; import com.zhangguo.springmvc08.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; /**用戶業務*/ @Service public class UserService { @Resource(name="mysql") IUserDAO userdao; public List<User> queryAllUsers(){ return userdao.getAll(); } public User getUserById(int id){ return userdao.getUserById(id); } public boolean deleteUser(int id){ return userdao.delete(id); } public boolean addUser(User user){ return userdao.add(user); } public boolean editUser(User user){ return userdao.update(user); } }
1.7.6、徹底解決Spring MVC 中文亂碼
添加用戶后發現有亂碼,調試發現發送到服務器的數據已經是亂碼
1、頁面編碼
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
2、URL中的亂碼
改tomcat中server.xml中Connector的port=“8080”,加上一個 URIEncoding=”utf-8”
3、配置過濾器,指定所有請求的編碼
修改web.xml,添加編碼過濾器
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4、文件編碼
將文件另存為utf-8格式
5、數據庫編碼
連接字符串指定編碼格式
public static String URL="jdbc:mysql://127.0.0.1:3306/mvcdb?useUnicode=true&characterEncoding=UTF-8"
創建數據庫時指定utf-8編碼格式
最終運行結果正常:
二、RESTful
2.1、概要
REST(英文:Representational State Transfer,簡稱REST,表述性狀態轉移)描述了一個架構樣式的網絡系統,比如 web 應用程序。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規范的主要編寫者之一。在目前主流的三種Web服務交互方案中,REST相比於SOAP(Simple Object Access protocol,簡單對象訪問協議)以及XML-RPC更加簡單明了,無論是對URL的處理還是對Payload的編碼,REST都傾向於用更加簡單輕量的方法設計和實現。值得注意的是REST並沒有一個明確的標准,而更像是一種設計的風格。
RESTful架構,就是目前最流行的一種互聯網軟件架構。它結構清晰、符合標准、易於理解、擴展方便,所以正得到越來越多網站的采用。
GET /tickets # 獲取ticket列表
GET /tickets/12 # 查看某個具體的ticket
POST /tickets # 新建一個ticket
PUT /tickets/12 # 更新ticket 12.
DELETE /tickets/12 #刪除ticekt 12
REST特點如下:
- 基於HTTP協議
- 是另一種服務架構
- 傳遞是JSON、POX(Plain Old XML)而不是SOAP格式的數據
- 充分利用HTTP謂詞(Verb)
- 側重數據的傳輸,業務邏輯交給客戶端自行處理
REST是一種分布式服務架構的風格約束,像Java、.Net(WCF、WebAPI)都有對該約束的實現,使URL變得更加有意義,更加簡潔明了,如:
http://www.zhangguo.com/products/1 get請求 表示獲得所有產品的第1個
http://www.zhangguo.com/products/product post請求 表示添加一個產品
http://www.zhangguo.com/products/1/price get請求 表示獲得第1個產品的價格
http://www.zhangguo.com/products/1 delete請求 刪除編號為1的產品
REST設計需要遵循的原則:
- 網絡上的所有事物都被抽象為資源(resource);
- 每個資源對應一個唯一的資源標識符(resource identifier);
- 通過通用的連接器接口(generic connector interface)對資源進行操作;
- 對資源的各種操作不會改變資源標識符;
- 所有的操作都是無狀態的(stateless)
謂詞
GET
表示查詢操作,相當於Retrieve、Select操作
POST
表示插入操作,相當於Create,Insert操作
PUT
表示修改操作,相當於Update操作
DELETE
表示刪除操作,相當於Delete操作
其它還有:
2.2、@RestController
Spring 4.0重要的一個新的改進是@RestController注解,它繼承自@Controller注解。4.0之前的版本,Spring MVC的組件都使用@Controller來標識當前類是一個控制器servlet。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { String value() default ""; }
使用這個注解,我們可以開發REST服務的時候不需要使用@Controller而專門的@RestController。
當你實現一個RESTful web services的時候,response將一直通過response body發送。為了簡化開發,Spring 4.0提供了一個專門版本的controller。
添加了AsyncRestTemplate類,當開發REST客戶端時允許非阻塞異步支持。
2.2.1、Hello World
默認控制器與Action
package com.zhangguo.springmvc08.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller //聲明為控制器 @RequestMapping(path = {"/","Home","First"}) //請求映射 public class HomeController { @RequestMapping(path = "/index") //請求映射 public String index(Model model){ model.addAttribute("message","Hello Spring MVC!"); return "home/index"; } @RequestMapping(path = "/") //請求映射 public String first(Model model){ model.addAttribute("message","Hello Spring MVC,Welcome Page!"); return "home/index"; } }
修改pom.xml添加jackson的引用
新增一個控制器,代碼如下:
package com.zhangguo.springmvc08.controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(path="/users") public class UsersController { @RequestMapping(path = "/{name}",method = RequestMethod.GET) public String hello(@PathVariable String name){ return "Hello "+name; } @RequestMapping(path = "/stu/{name}",method = RequestMethod.GET) public Student student(@PathVariable String name){ return new Student("Hello "+name); } } /**學生*/ class Student{ public Student(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
運行結果:
從上面的示例可以看出,使用@RestController后返回的字符串不再是路徑,如果返回的是對象則會直接序列化,可以是JSON或XML;如果返回的是對象類型則直接序列化成JSON格式,請注意添加對Jackson的依賴。
2.3、RESTful員工管理示例
假定要為員工(emp)提供對外的REST服務,接口如下:
/emps get 獲得所有的員工信息
/emps/1 get 獲得編號為1的員工信息
/emps post 添加
/emps put 修改
/emps/1 delete 刪除
2.3.1、獲得所有的員工信息服務
/emps get 獲得所所有的員工信息
package com.zhangguo.springmvc08.controller; import com.zhangguo.springmvc08.entity.User; import com.zhangguo.springmvc08.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping(path = "/emps") public class EmpController { @Autowired UserService userService; @RequestMapping(path = "", method = RequestMethod.GET) public List<User> getAllemps() { return userService.queryAllUsers(); } }
結果:
2.3.2、獲得指定編號的員工信息服務
/emps/1 get 獲得編號為1的員工信息
代碼:
@RequestMapping(path = "/{id}", method = RequestMethod.GET) public User getEmpById(@PathVariable int id) { return userService.getUserById(id); }
結果:
2.3.3、新增員工服務
/emps post 添加
代碼:
@RequestMapping(path = "", method = RequestMethod.POST) public boolean addEmp(@RequestBody User user) { return userService.addUser(user); }
請求:
返回true
結果:
說明:參數中的json格式一定要使用標准格式,注意引號,注意Content-Type,默認的Content-Type類型是:application/x-www-form-urlencoded
因為我們使用json,則Content-Type的值應該為application/json;charset=utf-8
2.3.4、修改員工服務
/emps 修改 put請求
代碼:
@RequestMapping(path = "", method = RequestMethod.PUT) public boolean updateEmp(@RequestBody User user) { return userService.editUser(user); }
測試:
結果:
2.3.3、刪除員工服務
/emps/1 delete 刪除
代碼:
package com.zhangguo.springmvc08.controller; import com.zhangguo.springmvc08.entity.User; import com.zhangguo.springmvc08.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping(path = "/emps") public class EmpController { @Autowired UserService userService; @RequestMapping(path = "", method = RequestMethod.GET) public List<User> getAllEmps() { return userService.queryAllUsers(); } @RequestMapping(path = "/{id}", method = RequestMethod.GET) public User getEmpById(@PathVariable int id) { return userService.getUserById(id); } @RequestMapping(path = "", method = RequestMethod.POST) public boolean addEmp(@RequestBody User user) { return userService.addUser(user); } @RequestMapping(path = "", method = RequestMethod.PUT) public boolean updateEmp(@RequestBody User user) { return userService.editUser(user); } @RequestMapping(path = "/{id}", method = RequestMethod.DELETE) public AjaxState deleteEmpById(@PathVariable int id) { Boolean result=userService.deleteUser(id); return new AjaxState(result?"success":"error",id,result?"刪除成功!":"刪除失敗"); } } class AjaxState{ public String state; public Object data; public String message; public AjaxState(String state, Object data, String message) { this.state = state; this.data = data; this.message = message; } public AjaxState(){} }
測試:
結果:
已刪除成功,delete請求不需要正文與get請求類似
2.4、AJAX客戶端調用RESTful
ajax傳送json格式數據,關鍵是指定contentType,data要是json格式
如果是restful接口,把type改成對應的post(增)、delete(刪)、put(改)、get(查)即可
var post_data={"name":"test001","pass":"xxxx"}; $.ajax({ url: "http://192.168.10.111:8080/uc/login", type: 'post', contentType: "application/json; charset=utf-8", data:JSON.stringify(post_data), success:function (data) { //調用成功 }, error: function(data, textStatus, errorThrown){ //調用失敗 } });
為了前端統一調用,修改后的控制器如下:
package com.zhangguo.springmvc08.controller; import com.zhangguo.springmvc08.entity.User; import com.zhangguo.springmvc08.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping(path = "/emps") public class EmpController extends BaseController { @Autowired UserService userService; @RequestMapping(path = "", method = RequestMethod.GET) public AjaxState getAllEmps() { List<User> users=userService.queryAllUsers(); boolean result=users!=null; return new AjaxState(result?"success":"error",users,result?"獲得數據成功!":"獲得數據失敗!"); } @RequestMapping(path = "/{id}", method = RequestMethod.GET) public AjaxState getEmpById(@PathVariable int id) { User user=userService.getUserById(id); boolean result=user!=null; return new AjaxState(result?"success":"error",user,result?"獲得數據成功!":"獲得數據失敗!"); } @RequestMapping(path = "", method = RequestMethod.POST) public AjaxState addEmp(@RequestBody User user) { boolean result=userService.addUser(user); return new AjaxState(result?"success":"error",user,result?"添加成功!":"添加失敗"); } @RequestMapping(path = "", method = RequestMethod.PUT) public AjaxState updateEmp(@RequestBody User user) { boolean result=userService.editUser(user); return new AjaxState(result?"success":"error",user,result?"修改成功!":"修改失敗"); } @RequestMapping(path = "/{id}", method = RequestMethod.DELETE) public AjaxState deleteEmpById(@PathVariable int id) { Boolean result=userService.deleteUser(id); return new AjaxState(result?"success":"error",id,result?"刪除成功!":"刪除失敗"); } } class AjaxState{ public String state; public Object data; public String message; public AjaxState(String state, Object data, String message) { this.state = state; this.data = data; this.message = message; } public AjaxState(){} }
2.4.1、用戶列表
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <h2>員工管理</h2> <table border="1" width="100%" id="tabEmps"> <tr> <th>編號</th> <th>姓名</th> <th>生日</th> <th>地址</th> <th>電話</th> <th>操作</th> </tr> </table> <p> </p> <p class="loading" style="display: none;"> <img src="img/loading.gif" align="absmiddle">努力加載中... </p> <p class="message"> </p> <script src="js/jquery-1.11.3.min.js"></script> <script> // var data = { // "state": "success", // "data": {"id": 1, "name": "張學友", "birthday": -41500800000, "address": "中國香港", "phone": "18989890098"}, // "message": "獲得數據成功!" // } var app = { url: "http://localhost:8080/mvc08/emps", init:function(){ this.binddata(); }, ajax: function (actionType, callback, path, data) { $.ajax({ url: app.url + (path||""), contentType: "application/json;charset=utf-8", data: data || {}, type: actionType||"get", dataType: "json", success: function (data) { if(data&&data.state=="success"){ app.info(data.message); }else if(data&&data.state=="error"){ app.info(data.message); }else{ app.info(data); } if(callback){ callback(data); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { info(textStatus+errorThrown); }, beforeSend: function () { $(".loading").show(200); } , complete: function () { $(".loading").hide(200); } }) ; }, binddata: function () { this.ajax("get",function(data){ $.each(data.data,function(index,emp){ var tr=$("<tr/>").appendTo("#tabEmps"); $("<td/>").text(emp.id).appendTo(tr); $("<td/>").text(emp.name).appendTo(tr); $("<td/>").text(emp.birthday).appendTo(tr); $("<td/>").text(emp.address).appendTo(tr); $("<td/>").text(emp.phone).appendTo(tr); $("<td/>").html("<a>刪除</a>").appendTo(tr); }); }); }, info:function(msg){ $(".message")[0].innerHTML+=msg+"<br/>"; } }; app.init(); </script> </body> </html>
結果:
2.4.2、新增用戶
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <h2>員工管理</h2> <table border="1" width="100%" id="tabEmps"> <tr> <th>編號</th> <th>姓名</th> <th>生日</th> <th>地址</th> <th>電話</th> <th>操作</th> </tr> </table> <p class="loading" style="display: none;"> <img src="img/loading.gif" align="absmiddle">努力加載中... </p> <form id="formEmps"> <fieldset> <legend>用戶信息</legend> <p> <label for="name">姓名:</label> <input name="name" id="name" type="text" required="required" maxlength="32"/> </p> <p> <label for="birthday">生日:</label> <input name="birthday" id="birthday" type="date" required="required" maxlength="8"/> </p> <p> <label for="address">地址:</label> <input name="address" id="address" type="text" required="required" maxlength="128"/> </p> <p> <label for="phone">電話:</label> <input name="phone" id="phone" type="text" required="required" maxlength="11"/> </p> <p> <input id="id" type="hidden" name="id" value=""/> <button type="button" id="btnSubmit">保存</button> </p> </fieldset> </form> <p class="message"> </p> <script src="js/jquery-1.11.3.min.js"></script> <script> // var data = { // "state": "success", // "data": {"id": 1, "name": "張學友", "birthday": -41500800000, "address": "中國香港", "phone": "18989890098"}, // "message": "獲得數據成功!" // } var app = { url: "http://localhost:8080/mvc08/emps", init: function () { $("#btnSubmit").click(app.save); this.binddata(); }, ajax: function (actionType, callback, path, data) { $.ajax({ url: app.url + (path || ""), contentType: "application/json;charset=utf-8", data:JSON.stringify(data)||"{}", type: actionType || "get", dataType: "json", success: function (data) { if (data && data.state == "success") { app.info(data.message); } else if (data && data.state == "error") { app.info(data.message); } else { app.info(data); } if (callback) { callback(data); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { app.info(textStatus + errorThrown); }, beforeSend: function () { $(".loading").show(200); } , complete: function () { $(".loading").hide(200); } }) ; }, binddata: function () { $("#tabEmps tr:gt(0)").remove(); this.ajax("get", function (data) { $.each(data.data, function (index, emp) { var tr = $("<tr/>").appendTo("#tabEmps"); $("<td/>").text(emp.id).appendTo(tr); $("<td/>").text(emp.name).appendTo(tr); $("<td/>").text(emp.birthday).appendTo(tr); $("<td/>").text(emp.address).appendTo(tr); $("<td/>").text(emp.phone).appendTo(tr); $("<td/>").html("<a>刪除</a>").appendTo(tr); }); }); }, getEmp:function(){ return { "id":$("#id").val(), "name":$("#name").val(), "birthday":$("#birthday").val(), "address":$("#address").val(), "phone":$("#phone").val() }; }, save:function(){ var emp=app.getEmp(); if(emp.id){ app.update(emp); }else{ app.add(emp); } }, add:function(emp){ app.ajax("POST",function (data) { app.binddata(); },"",emp); }, update:function(emp){ app.ajax("Put",function (data) { app.binddata(); },"",emp); }, info: function (msg) { $(".message")[0].innerHTML += msg + "<br/>"; } }; app.init(); </script> </body> </html>
結果:
2.4.3、刪除用戶
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <h2>員工管理</h2> <table border="1" width="100%" id="tabEmps"> <tr> <th>編號</th> <th>姓名</th> <th>生日</th> <th>地址</th> <th>電話</th> <th>操作</th> </tr> </table> <p class="loading" style="display: none;"> <img src="img/loading.gif" align="absmiddle">努力加載中... </p> <form id="formEmps"> <fieldset> <legend>用戶信息</legend> <p> <label for="name">姓名:</label> <input name="name" id="name" type="text" required="required" maxlength="32"/> </p> <p> <label for="birthday">生日:</label> <input name="birthday" id="birthday" type="date" required="required" maxlength="8"/> </p> <p> <label for="address">地址:</label> <input name="address" id="address" type="text" required="required" maxlength="128"/> </p> <p> <label for="phone">電話:</label> <input name="phone" id="phone" type="text" required="required" maxlength="11"/> </p> <p> <input id="id" type="hidden" name="id" value=""/> <button type="button" id="btnSubmit">保存</button> </p> </fieldset> </form> <p class="message"> </p> <script src="js/jquery-1.11.3.min.js"></script> <script> // var data = { // "state": "success", // "data": {"id": 1, "name": "張學友", "birthday": -41500800000, "address": "中國香港", "phone": "18989890098"}, // "message": "獲得數據成功!" // } var app = { url: "http://localhost:8080/mvc08/emps", init: function () { $("#btnSubmit").click(app.save); $("#tabEmps").on("click", ".del", app.delete); this.binddata(); }, ajax: function (actionType, callback, path, data) { $.ajax({ url: app.url + (path || ""), contentType: "application/json;charset=utf-8", data: JSON.stringify(data) || "{}", type: actionType || "get", dataType: "json", success: function (data) { if (data && data.state == "success") { app.info(data.message); } else if (data && data.state == "error") { app.info(data.message); } else { app.info(data); } if (callback) { callback(data); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { app.info(textStatus + errorThrown); }, beforeSend: function () { $(".loading").show(200); } , complete: function () { $(".loading").hide(200); } }) ; }, binddata: function () { $("#tabEmps tr:gt(0)").remove(); this.ajax("get", function (data) { $.each(data.data, function (index, emp) { var tr = $("<tr/>").data("emp", emp).appendTo("#tabEmps"); $("<td/>").text(emp.id).appendTo(tr); $("<td/>").text(emp.name).appendTo(tr); $("<td/>").text(emp.birthday).appendTo(tr); $("<td/>").text(emp.address).appendTo(tr); $("<td/>").text(emp.phone).appendTo(tr); $("<td/>").html("<a class='del' href='#'>刪除</a>").appendTo(tr); }); }); }, getEmp: function () { return { "id": $("#id").val(), "name": $("#name").val(), "birthday": $("#birthday").val(), "address": $("#address").val(), "phone": $("#phone").val() }; }, save: function () { var emp = app.getEmp(); if (emp.id) { app.update(emp); } else { app.add(emp); } }, add: function (emp) { app.ajax("POST", function (data) { app.binddata(); }, "", emp); }, update: function (emp) { app.ajax("Put", function (data) { app.binddata(); }, "", emp); }, delete: function () { if (confirm("刪除嗎?")) { var tr = $(this).closest("tr"); var emp = tr.data("emp"); app.ajax("DELETE", function (data) { tr.remove(); }, "/" + emp.id); } }, info: function (msg) { $(".message")[0].innerHTML += msg + "<br/>"; } }; app.init(); </script> </body> </html>
結果:
2.4.4、更新數據
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <h2>員工管理</h2> <table border="1" width="100%" id="tabEmps"> <tr> <th>編號</th> <th>姓名</th> <th>生日</th> <th>地址</th> <th>電話</th> <th>操作</th> </tr> </table> <p class="loading" style="display: none;"> <img src="img/loading.gif" align="absmiddle">努力加載中... </p> <form id="formEmps"> <fieldset> <legend>用戶信息</legend> <p> <label for="name">姓名:</label> <input name="name" id="name" type="text" required="required" maxlength="32"/> </p> <p> <label for="birthday">生日:</label> <input name="birthday" id="birthday" type="date" required="required" maxlength="8"/> </p> <p> <label for="address">地址:</label> <input name="address" id="address" type="text" required="required" maxlength="128"/> </p> <p> <label for="phone">電話:</label> <input name="phone" id="phone" type="text" required="required" maxlength="11"/> </p> <p> <input id="id" type="hidden" name="id" value=""/> <button type="button" id="btnSubmit">保存</button> </p> </fieldset> </form> <p class="message"> </p> <script src="js/jquery-1.11.3.min.js"></script> <script> // var data = { // "state": "success", // "data": {"id": 1, "name": "張學友", "birthday": -41500800000, "address": "中國香港", "phone": "18989890098"}, // "message": "獲得數據成功!" // } var app = { url: "http://localhost:8080/mvc08/emps", init: function () { $("#btnSubmit").click(app.save); $("#tabEmps").on("click", ".del", app.delete); $("#tabEmps").on("click", ".edit", app.edit); this.binddata(); }, ajax: function (actionType, callback, path, data) { $.ajax({ url: app.url + (path || ""), contentType: "application/json;charset=utf-8", data: JSON.stringify(data) || "{}", type: actionType || "get", dataType: "json", success: function (data) { if (data && data.state == "success") { app.info(data.message); } else if (data && data.state == "error") { app.info(data.message); } else { app.info(data); } if (callback) { callback(data); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { app.info(textStatus + errorThrown); }, beforeSend: function () { $(".loading").show(200); } , complete: function () { $(".loading").hide(200); } }) ; }, binddata: function () { $("#tabEmps tr:gt(0)").remove(); this.ajax("get", function (data) { $.each(data.data, function (index, emp) { var tr = $("<tr/>").data("emp", emp).appendTo("#tabEmps"); $("<td/>").text(emp.id).appendTo(tr); $("<td/>").text(emp.name).appendTo(tr); $("<td/>").text(emp.birthday).appendTo(tr); $("<td/>").text(emp.address).appendTo(tr); $("<td/>").text(emp.phone).appendTo(tr); $("<td/>").html("<a class='del' href='#'>刪除</a> | <a class='edit' href='#'>編輯</a>").appendTo(tr); }); }); }, getEmp: function () { return { "id": $("#id").val(), "name": $("#name").val(), "birthday": $("#birthday").val(), "address": $("#address").val(), "phone": $("#phone").val() }; }, save: function () { var emp = app.getEmp(); if (emp.id) { $("#id").val(""); app.update(emp); } else { app.add(emp); } }, add: function (emp) { app.ajax("POST", function (data) { app.binddata(); }, "", emp); }, update: function (emp) { app.ajax("Put", function (data) { app.binddata(); }, "", emp); }, delete: function () { if (confirm("刪除嗎?")) { var tr = $(this).closest("tr"); var emp = tr.data("emp"); app.ajax("DELETE", function (data) { tr.remove(); }, "/" + emp.id); } }, edit:function(){ var emp = $(this).closest("tr").data("emp"); $("#id").val(emp.id); $("#name").val(emp.name); $("#birthday").val(emp.birthday); $("#address").val(emp.address); $("#phone").val(emp.phone); }, info: function (msg) { $(".message")[0].innerHTML += msg + "<br/>"; } }; app.init(); </script> </body> </html>
結果:
三、示例下載
https://zhangguo5.coding.net/public/SpringMVC08/SpringMVC08/git
四、視頻
https://www.bilibili.com/video/av16991874/
五、作業
5.1、請練習上課示例
5.2、請使用Spring MVC對外提供商品(Product)的管理接口,如:
product/list 獲得所有商品 get
product/1 獲得編號為1的商品 get
product/delete/1 刪除編號為1的商品 get
product/insert 新增商品 post
product/edit 編輯商品 post
使用AJAX調用發布的服務,實現如下功能,驗證、搜索、多刪除功能選作。
5.3、請完成一個前后台分離的汽車管理系統(CarSystem),需要管理汽車的(車牌、顏色、價格、出廠日期、排量),要求完成CRUD功能,界面美觀大方。
a)、請使用MySQL數據庫創建庫與表(CarSystem)
b)、使用Spring MVC定義5個RESTful服務,注意路徑格式,先用fiddler測試通過。
c)、定義car.html頁面,使用jQuery插件中的ajax功能消費RESTful服務實現功能,反復測試。