Spring MVC 学习总结(九)——Spring MVC实现RESTful与JSON(Spring MVC为前端提供服务)
很多时候前端都需要调用后台服务实现交互功能,常见的数据交换格式多是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服务实现功能,反复测试。