SpringMVC - 请求处理

SpringMVC是如何处理请求的,主要讲解对请求中携带的参数的接收和处理。

1. @RequestMapping

1.1 作用范围

@RequestMapping注解可以用在方法(METHOD)上,也可以用在类(TYPE)上。

1.2 作用

SpringMVC框架使用@RequestMapping注解为后端控制器方法指定可以处理哪些请求。

注解在类上时,@RequestMapping提供初步的请求映射信息。相对于web应用的根目录。

注解在方法上时,@RequestMapping提供进一步的细分映射信息。相对于类定义出的URL。
若类上并没有使用@RequestMapping标注,则方法上标记的URL相对于web应用的根目录。

DispatcherServlet拦截请求后就通过后端控制器方法上提供的映射信息确定请求所对应的处理方法。

1.3 属性

  1. value/path:指定该方法可以处理哪些请求。
    值为String数组。

    不能有多个方法映射同一个请求,否则会抛出异常。

    一个方法可以映射多个请求。
  2. method:指定该方法处理的请求方式

    值为RequestMethod枚举类型:

    GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE;
  3. param:指定该方法处理的请求需要哪些参数。
    值为String数组。

    支持简单的表达式:

    user:请求必须带有名为user的参数,不带报错。

    !user:请求不能包含名为user的参数,带了报错。

    user=value:请求必须带有名为user的参数且值必须为value,不带此参数或参数值不匹配报错。

    user!=value:请求不能带有名为user且值为value的参数,不带此参数或者参数值不匹配都不会报错,若携带此参数且值为123报错。

    当值为多个参数时,必须同时满足才能被方法处理。否则报400错误。
  4. header:指定该方法处理的请求头包含哪些参数。
    值为String数组。

    支持简单的表达式,与param类似。
  5. consumes:指定该方法只处理哪种请求,规定请求头中的Content-Type。
  6. produces:指定返回的内容类型,给响应头中加上Content-Type。

2. SpringMVC接收参数的默认方式

SpringMVC接收请求参数的默认方式,
是在后端控制器的方法加上一个和请求参数名相同的参数。

请求URL:

xxx.jsp/xxx.html:

<a href="findUser?id=1">request</a>

后端控制器方法:

xxxController.java:

@RequestMapping("/findUser")
public String findUser(int id // 参数名和请求参数名相同){
    System.out.println("查找id为"+id+"的用户。");
    return"success";
}

方法参数名和请求参数名必须相同才能成功接收到参数。

当请求中不存在此参数或者参数名不正确,方法参数值就为null。
当请求中存在此参数但参数无值时(findUser?id=),方法参数值为空("")

3. @RequestParam

3.1 作用

@RequestParam注解是对默认接收方法的增强。
使用方式和默认接收方法几乎一样。

相当于request.getParameter();

3.2 作用范围

  • 方法的参数(ElementType.PARAMETER)

3.3 属性

  • String name/value:指定接收的请求参数名。

    此处填要从请求中接收的参数的名称。
  • boolean required:指定请求中是否必须包含此参数。

    假如值为true,则若请求中没有此参数时,返回400错误。

    假如值为false,则若请求中没有此参数时,参数值为null。
  • String defaultValue:指定接收的参数的默认值。

    如果此属性没有指定,且接收的参数值为空,则默认值为:
    “\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n”

    因为不能在注解的属性中使用null来表示空值,所以用这段16位Unicode的字符串来表示空值。

3.4 使用方式

在后端控制器方法的参数前标注@RequestParam注解即可:

请求URL:

xxx.jsp/xxx.html:

<a href="findUser?id=1">request</a>

后端控制器方法:

xxxController.java:

@RequestMapping("/findUser")
public String findUser(@RequestParam("id") int user_id){
    System.out.println("查找id为"+id+"的用户。");
    return"success";
}

4. @RequestHeader

4.1 作用

@RequestHeader注解是从请求头中获取某个key的值给后端控制器的方法的参数。
使用方式和@RequestParam注解相似。

相当于request.getHeader();

4.2 作用范围

  • 方法的参数(ElementType.PARAMETER)

4.3 属性

  • String name/value:指定接收的请求头的key。

    此处要填请求头中的key。接收这个key的value。
  • boolean required:指定请求头中是否必须有此key。

    假如值为true,则若请求头中没有此key,返回400错误。

    假如值为false,则若请求头中没有此key,参数值为null。
  • String defaultValue:指定请求头中此key的默认值。

3.4 使用方式

此注解的使用方式和@RequestParam相同。

5. @CookieValue

5.1 作用

@CookieValue注解是从Cookie中获取某个key的值给后端控制器的方法的参数。
使用方式和@RequestParam注解相似。

相当于遍历request.getCookies()后用cookie.getName().getValue();

5.2 作用范围

  • 方法的参数(ElementType.PARAMETER)

5.3 属性

  • String name/value:指定要获取的Cookie的名称。
  • boolean required:指定请求中是否必须有此Cookie。

    假如值为true,则若请求中没有此Cookie,返回400错误。

    假如值为false,则若请求头没有此Cookie,参数值为null。
  • String defaultValue:指定此Cookie的默认值。

4.4 使用方式

此注解的使用方式和@RequestParam相同。

6. @RequestBody

6.1 作用

@RequestBody注解主要用来接收从前端传递到后端的json字符串中的数据(请求体数据)的。
GET方式无请求体,所以使用@RequestBody接收数据时,
前端不能使用GET方式提交数据,而是用POST方式进行提交。
在后端的同一个接收方法里,
@RequestBody与@RequestParam()可以同时使用,
@RequestBody最多只能有一个,而@RequestParam()可以有多个。

6.2 作用范围

  • 方法的参数(ElementType.PARAMETER)

6.3 属性

  • boolean required:指定是否必须有请求体。

6.4 使用方式

此注解的使用方式和@RequestParam相同。需要注意的是,
@RequestBody注解只能出现一次。

6.5 HTTP 415 错误处理

  • 没有在请求头中指定Content-Type为application/json
  • SpringMVC中接收json需要json序列化的依赖。
    没有此依赖可能会触发415错误:
pom.xml:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.12.2</version>
</dependency>

SpringBoot中自带此包所以不用再导入该依赖。

7. @PathVariable

7.1 作用

通过@PathVariable可以将URL中占位符参数绑定到后端控制器处理方法的参数上:
URL中的{xxx}占位符可以通过@PathVariable(“xxx”)绑定到方法的参数上。

带占位符的URL是Spring3.0新增的功能,
该功能在SpringMVC向REST目标挺近发展过程中具有里程碑的意义。

  • 占位符只能映射单层路径,不能映射多层路径。

7.2 作用范围

  • 方法参数(ElementType.PARAMETER)

7.3 属性

  1. String name/value:指定占位符名称。

    此值要与URL中占位符的名称保持相同。

    如果URL中占位符的名称与注解标记的参数的名称相同时,此属性可以省略。
  2. Boolean required:指定URL中的是否占位符必须有值。

7.4 使用方式

在URL中使用占位符{xxx},URL此部分的字符串会通过@PathVariable(“xxx”)赋给方法的参数。

举个栗子:

loginController:

@RequestMapping("/user/{id}/{password}")
public String login(
        @PathVariable String id, // 此处占位符名和参数名相同,name属性可以省略。
        @PathVariable("password") String pwd // 此处不同,不可省略
){
    System.out.println("登录成功!\n" + id + pwd);
    return "success";
}

7.5 REST

7.5.1 简介
  • REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用
  • 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI即为每一个资源的独一无二的识别符。
  • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
  • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。具体说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
7.5.2 作用

REST以简洁的URL地址来发请求。以使用不同的请求方式来区分对资源的不同操作。
GET请求对应查询资源,POST请求对应增加资源,PUT请求对应更改资源,
DELETE请求对应删除资源。

7.5.3 使用方式

REST风格URL实现增删改查:

  • /user/1 HTTP GET:查询id为1的用户
  • /user HTTP POST:添加用户
  • /user/1 HTTP PUT:更新id为1的用户
  • /user/1 HTTP DELETE:删除id为1的用户

但是浏览器只能发起GET和POST请求,无法发起PUT和DELETE请求。
为了实现REST,Spring提供了一个请求方式的过滤器HiddenHttpMethodFilter,
可以将POST请求方式转化为PUT和DELETE请求。配置方式如下:

web.xml:
        
<!-- 请求方式过滤器 -->
<filter>
   <filter-name>methodFilter</filter-name>
   <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>methodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

让浏览器模拟发起PUT和DELETE请求:
使用POST请求的表单,表单携带一个隐藏项:_method,值为PUT和DELETE,
对应PUT和DELETE请求。发起请求后过滤器会根据隐藏项_method的值来区分PUT和DELETE请求。
示例如下:

xxx.jsp/xxx.html:

<form action="xxx" method="post">
   
   <!-- _method隐藏项的写法,此处的值为PUT,说明此请求为PUT请求 -->
   <input type="hidden" name="_method" value="PUT">
   
   <input type="text" name="name1" value="value1">
   <input type="text" name="name2" value="value2">
   <input type="submit" value="submit">
</form>

*关于高版本Tomcat(8.0及以上)转发PUT和DELETE请求至jsp页面,
显示405错误的情况:

问题:高版本Tomcat的jsp只支持GET,POST和HEAD请求方式,其他请求方式不受支持。

解决方法:给结果页开头<%@ %>内部添加isErrorPage=“true”;即可。

8. 使用原生 Servlet API 处理请求

8.1 作用

在后端控制器方法里,原生 Servlet API 对象可直接作为参数使用。

能够使用的对象有:

  1. HttpServletRequest(常用)
  2. HttpServletResponse(常用)
  3. HttpSession(常用)
  4. java.security.Principal(与安全协议有关的,不常用)
  5. Locale(与国际化有关的,不常用)
  6. InputStream(request.getInputStream();)
  7. OutputStream(response.getOutputStream();)
  8. Reader(request.getReader();)
  9. Writer(response.getWriter();)

8.2 使用方式

直接在后端控制器方法参数中使用即可:

xxxController.java:

@RequestMapping("/xxx")
public String xxxHandle(HttpServletRequest request){
    System.out.println(request.getParameter("id"));
    return "success";
}