接下来学习RESTful相关的知识,以及如何在SpringMVC中使用RESTful。在此之前先介绍一些什么是RESTful?REST(Representational State Transfer)表述性状态转移,REST并不是一种创新技术,它指的是一组架构约束条件和原则,符合REST的约束条件和原则的架构,我们就称之为RESTful架构。
RESTful的核心内容有三点:1、强调了资源和URI的关系(一一对应);2、资源在客户端和服务端之间的传送称之为资源的表述;3、资源在客户端发生变迁,进入到后续的状态,强调与之前状态不同称之为状态转移。总结起来就是一句话:资源在网络中以某种表现形式进行状态转移。
RESTful架构的特点:1、统一了客户端访问资源的接口;2、URL更加简洁,易于理解和便于扩展;3、有利于不同系统之间的资源共享。
RESTful具体来讲就是HTTP协议的四种形式表示四种基本操作:1、GET:获取资源;2、POST:新建资源;3、PUT:修改资源;4、DELETE:删除资源。
以RESTful风格实现某个课程的增删改查功能的URL访问方式为: 查询课程:http://localhost:8080/id method='get'
,这种不像之间的需要构造URL为http://localhost:8080/get?id=1
;添加课程:http://localhost:8080/course method='post'
;删除课程:http://localhost:8080/id method='delete'
;修改课程:http://localhost:8080/course method='put'
.
环境搭建 使用Maven新建一个webapp项目,名称为springmvc_restful_base
。第一步,配置pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <dependencies> <!--springmvc相关--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!--servlet相关--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!--jstl标签库--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--Junit测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies>
第二步,配置web.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <web-app> <display-name>Archetype Created Web Application</display-name> <!--处理中文乱码--> <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> <!--让SpringMVC放开css访问控制,设置访问静态资源--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <!--让SpringMVC放开js访问控制,设置访问静态资源--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <!--所有的请求都会被SpringMVC进行拦截--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置SpringMVC文件所在的位置--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
第三步,配置springmvc.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--开启包自动扫描--> <context:component-scan base-package="com.envy"></context:component-scan> <mvc:annotation-driven > <!-- 消息转换器 --> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean> </mvc:message-converters> </mvc:annotation-driven> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
第四步,新建对应的文件夹,如图所示:
代码书写 一般来说资源有查询,新增,删除,更改四种类型分别对应HTTP协议中四类请求:GET,POST,DELETE,PUT。未声明情况下浏览器默认使用GET提交请求。需要注意的是,普通浏览器只支持GET,POST方式 ,其他请求方式如DELETE/PUT必须通过过滤器的支持才能实现。Spring自带了一个过滤器HiddenHttpMethodFilter,支持GET、POST、PUT、DELETE请求。可以将post请求转换为delete/put请求,具体配置为:打开web.xml文件,添加如下过滤器:
1 2 3 4 5 6 7 8 9 <!--过滤器支持DELETE/PUT--> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
接着在entity包中新建一个Course的实体类:
1 2 3 4 private int id; private String name; private double price; //setter和getter方法
接着在daoy包中新建一个CourseDao的类,这个类用于保存用户提交的信息,类似于数据库的作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.envy.dao; import com.envy.entity.Course; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; @Repository public class CourseDao { private Map<Integer, Course> courses = new HashMap<>(); //新增 public void add(Course course){ courses.put(course.getId(),course); } //查询全部 public Collection<Course> getALl(){ return courses.values(); } //查询某个 public Course getOne(int id){ return courses.get(id); } //修改,修改和新增操作代码一致,因为Map集合只要key一致,重复添加只是对前面的进行覆盖 public void update(Course course){ courses.put(course.getId(),course); } //删除 public void delete(int id){ courses.remove(id); } }
然后在controller包中新建一个CourseController的类,并使用@Controller
注解表明这是Controller层,接着在里面定义增加课程的方法(添加课程是post请求,因此需要使用@PostMapping
注解):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.envy.controller; import com.envy.dao.CourseDao; import com.envy.entity.Course; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; @Controller public class CourseController { @Autowired private CourseDao courseDao; //添加课程 @PostMapping(value = "/add") public String add(Course course){ courseDao.add(course); return "redirect:/getAll"; } }
注意这里我们只是重定向到/getAll
页面,因此返回的是一个String类型的字符串,而不是ModelAndView对象。然后新建add.jsp页面,让这个jsp页面提交后由这个方法来进行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false" %> <html> <head> <title>add</title> <link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> body{ overflow-x:hidden; } #main{ width:1200px; height:600px; margin-left:500px; } </style> </head> <body> <div id="main"> <!-- 标题 --> <div class="row"> <div class="col-md-12"> <h1>添加课程</h1> </div> </div> <form class="form-horizontal" role="form" action="${pageContext.request.contextPath}/add" method="post"> <div class="form-group"> <label class="col-sm-1 control-label">课程编号</label> <div class="col-sm-3"> <input type="text" class="form-control" name="id" placeholder="请输入课程编号"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">课程名称</label> <div class="col-sm-3"> <input type="text" class="form-control" name="name" placeholder="请输入课程名称"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">课程价格</label> <div class="col-sm-3"> <input type="text" class="form-control" name="price" placeholder="请输入课程价格"> </div> </div> <div class="form-group"> <div class="col-sm-offset-1 col-sm-3"> <button type="submit" class="btn btn-default">提交</button> </div> </div> </form> </div> </body> </html>
注意表单中name属性的值必须与Course实体类所定义的属性一致,否则数据绑定的时候可能会出现某个属性为null的情况。同时action必须通过${pageContext.request.contextPath}/add
这种方式进行获取,并注明method="post"
。
既然添加课程后该页面会重定向至getAll页面,那么接下来就配置getAll方法(这里我们需要将查询的全部课程在index.jsp
页面上进行展示,因此需要返回一个ModelAndView
对象,同时新建index.jsp
页面):
1 2 3 4 5 6 7 8 //查询全部课程 @GetMapping(value = "/getAll") public ModelAndView getAll(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("courses",courseDao.getALl()); modelAndView.setViewName("index"); return modelAndView; }
新建的index.jsp
页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>课程列表</title> <link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <!-- 标题 --> <div class="row"> <div class="col-md-12"> <h1>课程管理</h1> </div> </div> <!-- 显示表格数据 --> <div class="row"> <div class="col-md-12"> <table class="table table-hover" id="emps_table"> <thead> <tr> <th> <input type="checkbox" id="check_all"/> </th> <th>编号</th> <th>课程名</th> <th>价格</th> <th>编辑</th> <th>删除</th> </tr> </thead> <tbody> <c:forEach items="${courses}" var="course"> <tr> <td><input type='checkbox' class='check_item'/></td> <td>${course.id}</td> <td>${course.name}</td> <td>${course.price}</td> <td> <form action="${pageContext.request.contextPath}/get/${course.id}" method="get"> <button class="btn btn-primary btn-sm edit_btn" type="submit"> <span class="glyphicon glyphicon-pencil">编辑</span> </button> </form> </td> <td> <form action="${pageContext.request.contextPath}/delete/${course.id}" method="post"> <button class="btn btn-danger btn-sm delete_btn" type="submit"> <%--过滤器和隐藏域结合使post请求转换成put请求--%> <input type="hidden" name="_method" value="DELETE"/> <span class="glyphicon glyphicon-trash">删除</span> </button> </form> </td> </tr> </c:forEach> </tbody> </table> </div> </div> </div> </body> </html>
然后观察到index.jsp
页面有一个编辑,一个删除按钮,先定义一个通过id查询课程的方法,只有先查询到某一课程,才能对其进行修改,并完善跳转url(实际上此处已经配置好了url处理路径)。既然是根据id来查询课程,因此方法参数必须是id,为了保证请求path路径中包含id这个属性,必须使用@PathVariable
注解来接收请求路径中占位符的值即id的值。
1 2 3 4 5 6 7 8 //通过id来查询某个课程 @GetMapping(value = "/get/{id}") public ModelAndView get(@PathVariable(value = "id")int id){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("course",courseDao.getOne(id)); modelAndView.setViewName("edit"); return modelAndView; }
需要将查询到的课程在edit.jsp
页面上进行展示,因此需要返回一个ModelAndView
对象,同时新建edit.jsp
页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false" %> <html> <head> <title>add</title> <link href="${pageContext.request.contextPath}/css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> body{ overflow-x:hidden; } #main{ width:1200px; height:600px; margin-left:500px; } </style> </head> <body> <div id="main"> <!-- 标题 --> <div class="row"> <div class="col-md-12"> <h1>编辑课程</h1> </div> </div> <form class="form-horizontal" role="form" action="${pageContext.request.contextPath}/update" method="post"> <div class="form-group"> <label class="col-sm-1 control-label">课程编号</label> <div class="col-sm-3"> <input type="text" class="form-control" name="id" value="${course.id}" readonly="readonly" placeholder="请输入课程编号"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">课程名称</label> <div class="col-sm-3"> <input type="text" class="form-control" name="name" value="${course.name}" placeholder="请输入课程名称"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">课程价格</label> <div class="col-sm-3"> <input type="text" class="form-control" name="price" value="${course.price}" placeholder="请输入课程价格"> </div> </div> <div class="form-group"> <div class="col-sm-offset-1 col-sm-3"> <%--过滤器和隐藏域结合使post请求转换成put请求--%> <input type="hidden" name="_method" value="PUT"> <button type="submit" class="btn btn-default">提交</button> </div> </div> </form> </div> </body> </html>
注意在编辑页面中,课程编号也就是其id是不可编辑的,因此在html标签上添加 readonly="readonly"
属性,既然已经查到了其信息,那么后面便是修改课程信息了。
我们知道修改完,页面还是重定向到/getAll
页面,因此返回的是一个String类型的字符串,而不是ModelAndView对象。同时修改对象在RESTful中是PUT,而SpringMVC默认不支持,于是我们需要在web.xml文件添加HiddenHttpMethodFilter
过滤器(顾名思义就是隐藏http请求方法的过滤器):
1 2 3 4 5 6 7 8 9 <!--过滤器支持DELETE/PUT--> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
然后定义修改方法:
1 2 3 4 5 6 //修改课程 @PutMapping(value = "/update") public String update(Course course){ courseDao.update(course); return "redirect:/getAll"; }
注意里面必须使用@PutMapping
注解,参数是Course对象。注意要想实现put,仅仅是配置过滤器是不行的,还需要在jsp页面中添加隐藏域,如put对应的隐藏域为:
1 <input type="hidden" name="_method" value="PUT"/>
一般隐藏域都是放在button
按钮的上方,就像这样:
1 2 <input type="hidden" name="_method" value="PUT"> <button type="submit" class="btn btn-default">提交</button>
然后在edit.jsp
的form表单的action属性配置待修改课程的id以及方法:
1 action="${pageContext.request.contextPath}/update" method="post"
接着回到index.jsp
页面,配置删除课程的URL:
1 2 3 4 5 6 7 <form action="${pageContext.request.contextPath}/delete/${course.id}" method="post"> <button class="btn btn-danger btn-sm delete_btn" type="submit"> <%--过滤器和隐藏域结合使post请求转换成put请求--%> <input type="hidden" name="_method" value="DELETE"/> <span class="glyphicon glyphicon-trash">删除</span> </button> </form>
然后在CourseController
类中定义删除方法:
1 2 3 4 5 6 //删除某个课程 @DeleteMapping(value = "/delete/{id}") public String delete(@PathVariable(value = "id")int id){ courseDao.delete(id); return "redirect:/getAll"; }
同样删除课程也是需要传入id的值,因此必须使用@PathVariable
注解来接收请求路径中占位符的值即id的值。最后运行该项目,测试各种方法,发现均正常显示。