REST以及Spring对REST的支持

下图是学习REST风格接口,然后通过SpringMVC实现的一个课程管理系统的效果图。 那么RESTful是什么呢?其实简言之看Url就知道要什么、看Http method就知道干什么、看Http status code就知道结果如何。

大家都知道"古代"网页是前端后端融在一起的,比如之前的PHP,JSP等。在之前的桌面时代问题不大,但是近年来移动互联网的发展,各种类型的Client层出不穷,RESTful可以通过一套统一的接口为 Web,iOS和Android提供服务。另外对于广大平台来说,比如Facebook platform,微博开放平台,微信公共平台等,它们不需要有显式的前端,只需要一套提供服务的接口,于是RESTful更是它们最好的选择。

REST风格概述

REST风格的服务呢:REST(Representational State Transfer),中文可以叫表述性状态转移! 如你所见,RESTful其实是为了只提供一套统一的接口,让其他任何需要访问资源的设备或者服务都从这个同一的接口写数据,取数据!

REST本质上是使用URL来访问资源种方式。URL就是我们平常使用的请求地址了,其中包括两部分:请求方式与请求路径,比较常见的请求方式是GET与POST,但在REST中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种,正好与CRUD(Create-Retrieve-Update-Delete,增删改查)四种操作相对应,例如,GET(查)、POST(增)、PUT(改)、DELETE(删),这正是REST与CRUD的异曲同工之妙!需要强调的是,REST是“面向资源”的,这里提到的资源,实际上就是我们常说的领域对象,在系统设计过程中,我们经常通过领域对象来进行数据建模。

REST是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据,当前请求不会受到上次请求的影响。也就是说,服务端将内部资源发布REST服务,客户端通过URL来访问这些资源,这不就是SOA所提倡的“面向服务”的思想吗?所以,REST也被人们看做是一种“轻量级”的SOA实现技术,因此在企业级应用与互联网应用中都得到了广泛应用。

REST 描述了一个架构样式的互联系统(如 Web 应用程序)。REST 约束条件作为一个整体应用时,将生成一个简单、可扩展、有效、安全、可靠的架构。由于它简便、轻量级以及通过 HTTP 直接传输数据的特性,RESTful Web 服务成为基于 SOAP 服务的一个最有前途的替代方案。用于 web 服务和动态 Web 应用程序的多层架构可以实现可重用性、简单性、可扩展性和组件可响应性的清晰分离。开发人员可以轻松使用 Ajax 和 RESTful Web 服务一起创建丰富的界面,相信这句话还是很能说明问题的!

REST实战

这个练手小项目就是正如上图所示,是个课程管理的项目,功能是查询课程,添加课程,修改课程,删除课程,CURD全齐了(为了说明主要问题,Dao层直接通过一个HashMap模拟了),下面把代码摆出来,摆代码之前先说说这个小项目的注意点:

  • 发一个重定向请求,注意格式问题: return "redirect:/getAll";
  • PostMapping 用于增删改查的
  • GetMapping 用于增删改查的
  • PutMapping 用于增删改查的
  • DeleteMapping 用于增删改查的
  • 写jsp的时候不要忘记EL表达式的支持
  • 引入css文件的时候,要么选择网络连接引入,要么配置css的servlet默认请求,否则会引入失败
  • 隐式DELETE请求和隐式PUT请求<input type="hidden" name="_method" value="DELETE"/><input type="hidden" name="_method" value="PUT"/>
  • Button做页面跳转的方式:<button type="button" class="btn btn-default" onclick = "window.location.href = 'getAdd'">

CourseController.java

 1@Controller
 2@RequestMapping("/")
 3public class CourseController {
 4    @Autowired
 5    private CourseDao courseDao;
 6
 7    @RequestMapping(value = "/getAdd")
 8    public ModelAndView getAdd(){
 9        ModelAndView modelAndView = new ModelAndView();
10        modelAndView.setViewName("add");
11        return modelAndView;
12    }
13
14    /**
15     * 添加课程
16     */
17    @PostMapping(value = "/add")
18    public String add(Course course){
19        courseDao.add(course);
20        //发一个重定向请求
21        return "redirect:/getAll";
22    }
23
24    /**
25     * 查询全部课程信息
26     */
27    @GetMapping(value = "/getAll")
28    public ModelAndView getAll(){
29        ModelAndView view = new ModelAndView();
30        view.setViewName("index");
31        view.addObject("courses", courseDao.getAll());
32        return view;
33    }
34
35    /**
36     * 通过id查询课程
37     */
38    @GetMapping(value = "/getById/{id}")
39    public ModelAndView getById(@PathVariable(value = "id") int id){
40        ModelAndView modelAndView = new ModelAndView();
41        modelAndView.setViewName("edit");
42        modelAndView.addObject("course", courseDao.getById(id));
43        return modelAndView;
44    }
45
46    /**
47     * 修改课程
48     */
49    @PutMapping(value = "/update")
50    public String update(Course course){
51        courseDao.update(course);
52        //发一个重定向请求
53        return "redirect:/getAll";
54    }
55    
56    /**
57     * 删除课程
58     */
59    @DeleteMapping(value = "/delete/{id}")
60    public String update(@PathVariable(value = "id")int id){
61        courseDao.delete(id);
62        //发一个重定向请求
63        return "redirect:/getAll";
64    }
65}

Dao层:

 1@Repository
 2public class CourseDao {
 3    private Map<Integer , Course> courses = new HashMap<>();
 4    /**
 5     * 新增课程
 6     */
 7    public void add(Course course){
 8        courses.put(course.getId(), course);
 9    }
10
11    /**
12     * 查询全部
13     */
14    public Collection<Course> getAll(){
15        return courses.values();
16    }
17
18    /**
19     * 根据id查询
20     */
21    public Course getById(int id){
22        return courses.get(id);
23    }
24
25    /**
26     * 修改课程
27     */
28    public void update(Course course){
29        courses.put(course.getId(), course);
30    }
31
32    /**
33     * 删除课程
34     */
35    public void delete(int id){
36        courses.remove(id);
37    }
38}

JavaBean:

1public class Course {
2    private int id;
3    private String name;
4    private double price;
5}

web.xml

 1
 2<web-app>
 3  <display-name>Archetype Created Web Application</display-name>
 4    <!-- 配置过滤器 -->
 5    <filter>
 6      <filter-name>hiddenHttpMethodFilter</filter-name>
 7      <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
 8    </filter>
 9
10    <filter>
11      <filter-name>encodingFilter</filter-name>
12      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
13      <init-param>
14        <!--要使用的字符集,一般我们使用UTF-8(保险起见UTF-8最好)-->
15        <param-name>encoding</param-name>
16        <param-value>UTF-8</param-value>
17      </init-param>
18      <init-param>
19        <!--是否强制设置request的编码为encoding,默认false,不建议更改-->
20        <param-name>forceRequestEncoding</param-name>
21        <param-value>false</param-value>
22      </init-param>
23      <init-param>
24        <!--是否强制设置response的编码为encoding,建议设置为true,下面有关于这个参数的解释-->
25        <param-name>forceResponseEncoding</param-name>
26        <param-value>true</param-value>
27      </init-param>
28    </filter>
29    <filter-mapping>
30      <filter-name>encodingFilter</filter-name>
31      <url-pattern>/*</url-pattern>
32    </filter-mapping>
33
34
35    <filter-mapping>
36      <filter-name>hiddenHttpMethodFilter</filter-name>
37      <url-pattern>/*</url-pattern>
38    </filter-mapping>
39
40    <servlet>
41      <servlet-name>springmvc</servlet-name>
42      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
43      <!-- 配置文件的路径 -->
44      <init-param>
45        <param-name>contextConfigLocation</param-name>
46        <param-value>classpath:springmvc.xml</param-value>
47      </init-param>
48    </servlet>
49
50    <servlet-mapping>
51      <servlet-name>default</servlet-name>
52      <url-pattern>*.css</url-pattern>
53    </servlet-mapping>
54
55    <servlet-mapping>
56      <servlet-name>springmvc</servlet-name>
57      <url-pattern>/</url-pattern>
58    </servlet-mapping>
59</web-app>

springmvc.xml

 1<!-- JSON数据解析 -->
 2<mvc:annotation-driven>
 3    <mvc:message-converters>
 4        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
 5        </bean>
 6    </mvc:message-converters>
 7</mvc:annotation-driven>
 8
 9<!-- 配置自动扫描IOC容器 -->
10<context:component-scan base-package="cn.edu.xpu"/>
11
12<!-- 配置视图解析器 -->
13<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
14    <!-- 前缀-->
15    <property name="prefix" value="/"></property>
16    <!-- 配置后缀-->
17    <property name="suffix" value=".jsp"></property>
18</bean>

index.jsp

 1<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 2<%@ page contentType="text/html;charset=UTF-8" language="java" %>
 3<%@ page isELIgnored="false" %>
 4<html>
 5<head>
 6    <title>Title</title>
 7    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
 8    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 9
10    <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
11    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
12
13    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
14    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
15
16</head>
17<body>
18
19<div class="container">
20    <!-- 标题 -->
21    <div class="row">
22        <div class="col-md-12">
23            <h1>课程管理<button type="button" class="btn btn-default"
24                            onclick = "window.location.href = 'getAdd'">增加课程</button></h1>
25        </div>
26    </div>
27    <!-- 显示表格数据 -->
28    <div class="row">
29        <div class="col-md-12">
30            <table class="table table-hover" id="emps_table">
31                <thead>
32                    <tr>
33                        <th>
34                            <input type="checkbox" id="check_all"/>
35                        </th>
36                        <th>编号</th>
37                        <th>课程名</th>
38                        <th>价格</th>
39                        <th>编辑</th>
40                        <th>删除</th>
41                    </tr>
42                </thead>
43                <tbody>
44                <c:forEach items="${courses}" var="course">
45                    <tr>
46                        <td><input type='checkbox' class='check_item'/></td>
47                        <td>${course.id}</td>
48                        <td>${course.name}</td>
49                        <td>${course.price}</td>
50                        <td>
51                            <form action="${pageContext.request.contextPath}/getById/${course.id}" method="get">
52                                <!-- 隐式PUT请求 -->
53                                <input type="hidden" name="_method" value="PUT"/>
54                                <button class="btn btn-primary btn-sm edit_btn">
55                                    <span class="glyphicon glyphicon-pencil">编辑</span>
56                                </button>
57                            </form>
58                        </td>
59                        <td>
60                            <form action="${pageContext.request.contextPath}/delete/${course.id}" method="post">
61                                <!-- 隐式DELETE请求 -->
62                                <input type="hidden" name="_method" value="DELETE"/>
63                                <button class="btn btn-danger btn-sm delete_btn">
64                                    <span class="glyphicon glyphicon-trash">删除</span>
65                                </button>
66                            </form>
67                        </td>
68                    </tr>
69                </c:forEach>
70                </tbody>
71            </table>
72        </div>
73    </div>
74
75</div>
76</body>
77</html>

edit.jsp

 1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
 2<%@ page isELIgnored="false" %>
 3<html>
 4<head>
 5    <title>Title</title>
 6    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
 7    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 8
 9    <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
10    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
11
12    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
13    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
14</head>
15<body>
16<div class="container">
17    <div class="row">
18        <form class="form-horizontal" role="form" action="${pageContext.request.contextPath}/update"  method="post">
19            <div class="form-group">
20                <label class="col-sm-1 control-label">课程编号</label>
21                <div class="col-sm-3">
22                    <input type="text" value="${course.id}" readonly="readonly" class="form-control" name="id" placeholder="请输入商品名称">
23                </div>
24            </div>
25            <div class="form-group">
26                <label class="col-sm-1 control-label">课程名称</label>
27                <div class="col-sm-3">
28                    <input type="text" value="${course.name}" class="form-control" name="name" placeholder="请输入商品价格">
29                </div>
30            </div>
31            <div class="form-group">
32                <label class="col-sm-1 control-label">课程价格</label>
33                <div class="col-sm-3">
34                    <input type="text" value="${course.price}" class="form-control" name="price" placeholder="请输入商品价格">
35                </div>
36            </div>
37            <div class="form-group">
38                <label class="col-sm-1 control-label"></label>
39                <div class="col-sm-3">
40                    <input type="hidden" name="_method" value="PUT"/>
41                    <button type="submit" class="btn btn-default" >提交</button>
42                </div>
43            </div>
44
45        </form>
46    </div>
47</div>
48</body>
49</html>

add.jsp

 1<%@ page contentType="text/html;charset=UTF-8" language="java" %>
 2<%@ page isELIgnored="false" %>
 3<html>
 4<head>
 5    <title>Title</title>
 6    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
 7    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 8
 9    <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
10    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
11
12    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
13    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
14</head>
15<body>
16
17
18<div class="col-md-12">
19    <form class="form-horizontal" role="form" action="${pageContext.request.contextPath}/add"  method="post">
20        <div class="form-group">
21            <label class="col-sm-1 control-label">课程编号</label>
22            <div class="col-sm-3">
23                <input type="text" class="form-control" name="id" placeholder="请输入商品名称">
24            </div>
25        </div>
26        <div class="form-group">
27            <label class="col-sm-1 control-label">课程名称</label>
28            <div class="col-sm-3">
29                <input type="text" class="form-control" name="name" placeholder="请输入商品价格">
30            </div>
31        </div>
32        <div class="form-group">
33            <label class="col-sm-1 control-label">课程价格</label>
34            <div class="col-sm-3">
35                <input type="text" class="form-control" name="price" placeholder="请输入商品价格">
36            </div>
37        </div>
38
39        <div class="form-group">
40
41            <label class="col-sm-1 control-label"></label>
42            <div class="col-sm-3">
43                <button type="submit" class="btn btn-default" >提交</button>
44            </div>
45        </div>
46    </form>
47</div>
48</body>
49</html>

pom.xml

 1<dependency>
 2  <groupId>javax.servlet</groupId>
 3  <artifactId>servlet-api</artifactId>
 4  <version>2.5</version>
 5</dependency>
 6<!-- JSTL -->
 7<dependency>
 8  <groupId>javax.servlet</groupId>
 9  <artifactId>jstl</artifactId>
10  <version>1.2</version>
11</dependency>
12<!-- JSTL实现包 -->
13<dependency>
14  <groupId>org.apache.taglibs</groupId>
15  <artifactId>taglibs-standard-impl</artifactId>
16  <version>1.2.5</version>
17</dependency>
18<dependency>
19  <groupId>org.springframework</groupId>
20  <artifactId>spring-webmvc</artifactId>
21  <version>5.1.5.RELEASE</version>
22</dependency>
23
24<dependency>
25  <groupId>junit</groupId>
26  <artifactId>junit</artifactId>
27  <version>4.11</version>
28  <scope>test</scope>
29</dependency>
30
31<dependency>
32  <groupId>com.fasterxml.jackson.core</groupId>
33  <artifactId>jackson-databind</artifactId>
34  <version>2.9.8</version>
35</dependency>