SpringMVC笔记(4)-SpringMVC中的RESTful与Ajax

RESTful

简介

RESTful是一种网络应用程序的设计风格和开发方式。REST全称为Representational State Transfer,表现层资源状态转移。在这种风格下,一切皆资源。它将服务器看作由很多离散的资源组成,每个资源是服务器上一个可命名的抽象概念,我们可以通过一个路径访问。而我们对于资源的操作,可能有增删改查等,则对应到HTTP协议中不同的请求方式。

简单来说,我们通过路径来表示我们需要访问的资源,而通过请求方式来表示对一个资源的不同操作。前面我们也提到,REST风格中,将传统意义上的参数用路径来表示,也符合其利用路径表示资源的思想。在HTTP协议中,我们可以使用的请求方式包括:GET获取资源、POST新建资源、PUT更新资源、DELETE删除资源,结合不同路径来决定操作含义:

操作 传统方式 REST风格
查询 /getUserById?id=1 get: /user/1
保存 /saveUser post: /user
删除 /deleteUser?id=1 delete: /user/1
更新 /updateUser put: /user

HiddenHttpMethodFilter

浏览器仅支持发送get和post的请求,如果要想要在SpringMVC中发送put和delete方式的请求,需要利用它提供的HiddenHttpMethodFilter类。这是SpringMVC提供的一个类,可以帮助我们将POST请求转换为DELETE或者PUT请求。

要使用它,我们需要先在web.xml中注册HiddenHttpMethodFilter

1
2
3
4
5
6
7
8
9
<!--配置SpringMVC HiddenHttpMethodFilter-->
<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>

注意,一般情况下我们也配置了SpringMVC的编码过滤器CharacterEncodingFilter,编码过滤器应该要配置在所有过滤器的前面。

要想经过HiddenHttpMethodFilter的请求,我们需要符合它的规则:

  1. 当前请求的请求方式必须为POST
  2. 当前请求必须传输请求参数_method,该参数的值为需要被转换为的请求方式,如putdelete

例如我们可以在前端设置下面的表单,这样实际传输的时候,会得到一个put请求方式的请求:

1
2
3
4
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改用户信息">
</form>

静态资源

回顾前面的操作,我们在SpringMVC的配置文件中配置了前端处理器DispatcherServlet,并且配置了它能够处理的请求路径为/,希望它能够匹配所有请求路径。但是在Tomcat的web.xml中,也配置了一个Servlet,它的url-pattern也是/,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

可以看到,我们配置的SpringMVC前端控制器DispatcherServlet与Tomcat中的默认配置器DefaultServlet配置的url-pattern相同,我们工程中的配置会覆盖掉Tomcat中的配置,也就是说所有的请求都会经过DispatcherServlet来处理。但是在Tomcat中,静态资源是通过DefaultServlet来处理的,而我们配置的前端控制器无法处理静态资源,因此我们在使用到静态资源的时候应该做相应的配置,需要在SpringMVC的配置文件中增加如下的配置:

1
2
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>

上面配置包括配置默认的Servlet,以及开启注解驱动。如果我们只配置了default-server-handler,则所有请求都会交给DefaultServlet来处理;而如果我们同时配置了注解驱动annotation-driven,那么所有的请求先交给DispatcherServlet处理,如果处理不了,例如静态资源等,则再交给DefaultServlet处理。

AJAX

ajax是一种重要的异步请求技术,SpringMVC中也提供了处理ajax的相关方法。我们主要关注的就是SpringMVC如何获取ajax请求中的参数,以及如果向前端响应相关数据,下面我们将分别介绍。

获取json请求参数

这里首先介绍axios携带参数的方式。axios在发送请求的时候,如果需要携带参数则可以指定使用参数paramsdata,它们两者有所区别:

  • params:表示以name1=value1&name2=value2的方式发送请求参数,不管使用的方式是get还是post,请求参数会被拼接到请求地址后。此种方式的参数可以通过request.getParameter()来获取,也可以用SpringMVC封装后形参的方式获取(见SpringMVC笔记(2)-@RequestMapping与请求参数获取 - EverNorif
  • data:表示以json格式发送请求参数,请求参数会被保存到请求报文的请求体中,传输到服务器。这类参数的获取需要先获取请求体中的数据,这会是一个json字符串,处理字符串的过程可以通过jar包,也可以通过SpringMVC的封装

这里我们主要关注后者,即如果获取json格式的数据。这部分数据以json的格式被保存在请求体中,我们无法通过前面的方式获取。而SpringMVC为我提供了方式来获取请求体信息,即@RequestBody注解。将该注解标注在字符串形参上,则该参数的内容即为请求体中的内容:

1
2
3
4
5
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody) {
System.out.println(requestBody);
return "success";
}

通过上面的方式,我们实际上已经可以获取到请求体中的json字符串,后面需要做的就是对json字符串进行解析。这一步我们可以使用现成的包来完成,例如fastJson等,但是SpringMVC也帮我们进行了一步封装,我们只需要指定承接RequestBody形参的类型不同,SpringMVC会自动帮我们进行转换,包括json转换为Map,json转换为实体类等。

要想开启对应的功能,我们需要先引入jackson的相关依赖:

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>

然后在SpringMVC的配置文件中开启mvc的注解驱动:

1
<mvc:annotation-driven/>

之后,我们只需要指定承接对象的类型即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 转化为Map对象
@RequestMapping("/testRequestBodyToMap")
public String testRequestBodyToMap(@RequestBody Map<String, Object> map) {
System.out.println(map);
return "success";
}

// 转化为对应的实体类对象
@RequestMapping("/testRequestBodyToUser")
public String testRequestBodyToUser(@RequestBody User user) {
System.out.println(user);
return "success";
}

返回json数据

在前面的项目中,我们写的每个控制器方法返回的都是一个视图,要么是字符串表示对应的视图名称,要么是ModelAndView对象,总之最后达到的都是页面跳转的方式。而在实际的场景中,我们的控制器方法更多的是作为一个数据的提供API,需要返回数据而不是网页。

类似JavaWeb中提到的,我们可以设置返回值为void,然后利用Response对象获取到字符输出流,然后向其中写入我们需要返回的数据,如下所示:

1
2
3
4
@RequestMapping("/testResponseWriter")
public void testResponseWriter(HttpServletResponse response) throws IOException {
response.getWriter().print("hello, I am data.");
}

上面是通过字符输出流实现的数据返回,实际上SpringMVC也给提供了相关注解来简化操作,即@ResponseBody。该注解用于标识控制器方法,表示将该方法的返回值作为响应报文的响应体响应到浏览器。此时的字符串返回值不再是表示视图名称,而是直接作为返回数据

1
2
3
4
5
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody() {
return "hello, I am data.";
}

在实际场景中,我们一般会以json格式向前端返回数据。通过上面的方式我们已经可以向前端返回数据了,所以我们只需要将需要返回的数据转化为json字符串的格式,然后向前端返回即可。转化为json字符串的工作可以通过现有的包来完成。当然,在SpringMVC中,@ResponseBody也帮我们进行了多一步的封装。我们可以在控制器方法中指定字符串之外的类型,在返回给前端的时候,会自动转化为json的格式。

当然如果要开启这个功能,也需要导入jackson的依赖,然后开启mvc的注解驱动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 响应集合类数据
@RequestMapping("/testReturnList")
@ResponseBody
public List<String> testReturnList() {
List<String> list = Arrays.asList("data1", "data2", "data3");
return list;
}

// 响应实体类数据
@RequestMapping("/testReturnUser")
@ResponseBody
public User testReturnUser() {
User admin = new User(123, "admin");
return admin;
}

@RestController:这个注解是SpringMVC提供的一个复合注解,标识在控制器的类上,相当于为类添加了@Controller注解,同时为其中的每个方法都添加了@ResponseBody注解


SpringMVC笔记(4)-SpringMVC中的RESTful与Ajax
http://example.com/2022/10/19/SpringMVC笔记-4-SpringMVC中的RESTful与Ajax/
作者
EverNorif
发布于
2022年10月19日
许可协议