深入理解 Spring MVC Controller —— 请求参数获取

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 前言接上篇《深入理解 Spring MVC Controller —— 请求映射》,上篇主要介绍了处理器方法及请求映射的定义。有了处理器方法 Spring MVC 就可以对请求进行处理,有了请求映射 Spring MVC 就能知道哪些请求应该由哪些处理器方法来处理。

前言


接上篇《深入理解 Spring MVC Controller —— 请求映射》,上篇主要介绍了处理器方法及请求映射的定义。有了处理器方法 Spring MVC 就可以对请求进行处理,有了请求映射 Spring MVC 就能知道哪些请求应该由哪些处理器方法来处理。


Spring MVC 中对请求进行处理,除了定义处理器方法以及请求映射,我们还需要关心的一个问题是如何接收请求有关的参数,因为不同的参数往往伴随着不同的业务逻辑。


由于请求参数获取的内容较多,这篇我们先来了解 Spring MVC 常见的参数获取方式,下篇我们再深入分析并实战如何利用请求参数优雅获取登录用户信息。


前方高能预警:作为基础内容,本篇篇幅略长,可先收藏等有时间再查阅。


请求参数获取


传统的 Java Web 项目,我们通常通过 HttpServletRequest 来获取请求相关的参数。Spring MVC 简化了请求参数的获取方式,直接将请求参数定义为处理器方法参数即可。


当处理器方法被 Spring MVC 调用时,Spring MVC 会尝试根据请求上下文信息解析出给定类型的方法参数值,并自动进行类型转换和参数校验,因此方法参数还可以定义为除字符串类型的其他类型。默认情况下处理器方法中可以定义的请求参数如下。


表单参数


表单参数可能是在 Spring MVC 中使用最频繁的请求参数之一了。


Http 协议中的表单大概可以分为两类,一类是 application/x-www-from-urlencoded,请求中只能携带字符串形式的参数值,另一类是 multipart/form-data,请求中除了携带字符串形式的参数值还可以携带文件。


Spring MVC 对字符串参数值和文件参数值有不同的处理方式。


表单单个普通参数获取


@RequestParam 注解指定请求参数

对于表单的单个普通参数获取,可以直接在处理器方法参数上添加 @RequestParam 注解并指定参数名,接收的是 GET 请求方式查询字符串上的参数或 POST 请求方式表单上的某一个参数。


Spring MVC 并未限制 @RequestParam 标注的处理器方法参数的类型,只要内部支持类型转换即可,方法参数类型通常来说为简单类型。简单类型包括基本类型及其包装类型、Enum、CharSequence、Number、Date、Temporal、URI、URL、Locale、Class 类型。


示例代码如下。

    @PostMapping("/login")
    public Result<LoginBO> login(@RequestParam("username") String username, @RequestParam("password") String password) {
        return Result.ok(userService.login(username, password));
    }

示例代码提供了登录功能,并获取了用户名和密码。


如果 JDK 版本在 1.8 以上使用 javac 编译时还可以添加 -parameters 参数,这样 @RequestParam 可以省略参数名,Spring MVC 会根据方法参数名获取参数值。


注意:如果想使用 Map 作为方法类型接收单个参数值,@RequestParam 必须指定参数名,否则 Map 方法参数接收的是所有的表单参数。


简单类型直接接收请求参数


类型为简单类型的处理器方法参数可以直接接收表单中的单个参数,方法参数名就是接收的参数名,注意此时方法参数上不能存在 @RequestParam 注解及 @RequestPart 注解。


表单单个文件参数获取

@RequestPart 指定文件参数

如果想要接收用户上传的文件,可以将 Multipart 相关类型直接定义为处理器方法参数的类型,然后使用 @RequestPart 指定请求参数名。


Multipart 相关的参数类型如下:


MultipartFile、Collection、List、MultipartFile[] 类型参数。

Part、Collection、List、Part[] 类型参数。

示例代码如下。

    @PostMapping("/upload")
    public Result<UploadBO> upload(@RequestPart("file") MultipartFile file) {
        return Result.ok(fileService.upload(file));
    }


Multipart 相关类型直接接收文件参数


除了使用 @RequestPart 注解指定参数名,还可以直接将 Multipart 相关类型直接定义为处理器方法参数,方法参数名就是请求参数名,注意此时方法参数上不能携带 @RequestParam 注解及 @RequestPart 注解。


所有表单参数获取


如果想要一次性获取所有的请求参数,而不是根据请求参数名获取,可以将处理器方法参数定义为 Map 或 MultiValueMap,然后使用 @RequestParam 注解标注方法参数,此时 @RequestParam 不能指定参数名。


具体可以使用的泛型类型如下。


MultiValueMap、MultiValueMap、MultiValueMap

Map、Map、Map

由于泛型的值只能为 String、MultipartFile、Part,因此普通表单参数和文件表单参数需要分别接收。


示例代码如下。


    @PostMapping("/upload")
    public Result<UploadBO> upload(@RequestParam Map<String, MultipartFile> files, @RequestParam Map<String, String> extra) {
        return null;
    }


可选请求参数


如果认为方法参数表示的表单请求参数是可选的,可以将 @RequestParamrequired 属性值指定为 false,表示该参数是可选的,除此之外还可以用 Optional 类型接收请求参数。示例代码如下。


    @PostMapping("/login")
    public Result<LoginBO> login(@RequestParam(value = "username", required = false) String username, @RequestParam("password") Optional<String> password) {
        return Result.ok(userService.login(username, password));
    }


请求头参数


Spring MVC 中,如果想要获取请求头中的单个参数值,可以使用 @RequestHeader 指定请求头,并标注在处理器方法参数上。与 @RequestParam 注解类似,请求头的名称也可以省略,此时将方法参数作为请求头的名称。示例代码如下。


    @PostMapping("/upload")
    public Result<UploadBO> upload(@RequestHeader("token") String token) {
        return null;
    }


如果想要一次性获取所有的请求头,可以使用 Map 类型作为处理器方法参数,Map 的 key 和 value 都为 String 类型,此时 @RequestHeader 注解不必指定请求头,示例代码如下。


    @PostMapping("/upload")
    public Result<UploadBO> upload(@RequestHeader Map<String, String> header1,@RequestHeader MultiValueMap<String, String> header2) {
        return null;
    }


默认情况 @RequestHeader 表示的请求头必须存在,如果是可选的,可以设置 @RequestHeader(required = false) 或使用 Optional 类型接收请求头。


Cookie 参数


请求头中可能包含多个 cookie 信息,如果直接获取 Cookie 请求头的值然后再解析会比较麻烦。


Spring MVC 支持使用 String 类型或 Cookie 类型作为处理器方法参数类型来接收 Cookie信息,在方法参数上添加 @CookieValue 注解指定 Cookie 的名称即可。


如果省略 Cookie 名称则方法参数名作为 Cookie 名称。示例代码如下。


    @PostMapping("/upload")
    public Result<UploadBO> upload(@CookieValue("username") String username, @CookieValue Cookie password) {
        return null;
    }


默认情况 @CookieValue 表示的 Cookie 必须存在,如果是可选的,可以设置 @CookieValue(required = false) 或使用 Optional 类型接收 Cookie。


路径变量


路径变量常用于 RESTFUL 风格的接口中,路径的值可以作为一个参数值,此时我们需要在映射路径中指定路径变量的名称,然后使用处理器方法参数接收。处理器方法参数上添加 @PathVariable 注解指定路径变量名即可,同样变量名也可以忽略,此时方法参数名作为路径变量名。示例代码如下。


    @GetMapping("/user/{userId}")
    public Result<?> getUser(@PathVariable("userId") String userId) {
        return null;
    }


默认情况 @PathVariable 表示的路径变量必须存在,如果是可选的,可以设置 @PathVariable(required = false) 或使用 Optional 类型接收路径变量。


矩阵变量


矩阵变量和路径变量类似,变量同样定义在路径中,但是使用较少。可以使用@MatrixVariable 注解标注的处理器方法参数接收矩阵变量,方法参数类型可以为简单类型,也可以为 Map。


Spring 5.2 版本及以下需要手动开启矩阵变量支持,配置如下。


@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}


处理器方法参数类型为 Map 的情况,@MatrixVariable 如果指定了 name,则 Map 接收的是单个矩阵变量的值。处理器方法参数获取单个矩阵变量值的示例代码如下。


    @GetMapping("/user{basic}/info")
    public Result<?> getUser(@MatrixVariable(name = "basic") String basic) {
        return Result.ok(basic);
    }


对于请求路径 /user;basic=zhangsan;/info,处理器方法参数 basic 接收到的值为 zhangsan。


处理器方法参数类型为 Map 的情况,@MatrixVariable 如果没有指定 name,可以使用 pathVar 指定请求映射路径中的变量名, 此时 Map 接收的是多个矩阵变量。处理器方法参数获取多个矩阵变量的示例代码如下。


    @GetMapping("/user{basic}/info")
    public Result<?> getUser(@MatrixVariable(pathVar = "basic") Map<String, String> basic) {
        return Result.ok(basic);
    }


对于请求路径 /user;name=zhangsan;age=20/info ,处理器方法参数 basic 接收到的值为 { "name": "zhangsan", "age": "20" }。


默认情况 @MatrixVariable 表示的矩阵变量必须存在,如果是可选的,可以设置 @MatrixVariable(required = false) 或使用 Optional 类型接收矩阵变量。


请求体参数


对于请求体,可以使用 @RequestBody 标注的处理器方法参数接收。示例代码如下。


@Data
public class User {
    private Long id;
    private String name;
}
@RestController
public class UserController {
    @GetMapping("/user/update")
    public Result<?> updateUser(@RequestBody User user) {
        return null;
    }
}


默认情况 @RequestBody 表示的请求头必须存在,如果是可选的,可以设置 @RequestBody(required = false) 或使用 Optional 类型接收请求体。


其他处理器方法参数


除了用于接收请求参数的处理器方法参数,Spring MVC 还支持定义一些其他的处理器方法参数。这里简单做一些介绍。


Request 相关参数


Request 相关参数表示 request 本身或其基本信息。


Spring MVC 支持的与 request 有关的处理器方法参数类型包括:

WebRequest、ServletRequest、MultipartRequest、HttpSession、PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId。

HttpEntity、RequestEntity。

SessionStatus。

除了上面的参数,Spring MVC 还支持获取 request attribute 中的参数,使用 @RequestAttribute 标注处理器方法参数即可,这可以获取过滤器或拦截中使用 ServletRequest#setAttribute 设置的参数。


Response 相关参数


resposne 相关参数表示 response 本身或其基本信息。


Spring MVC 支持的处理器方法参数类型包括: ServletResponse、OutputStream、Writer。


Model 相关参数


Model 是 MVC 中的概念,是 View 使用的数据,在 Spring MVC 中,不同的 View 实现,Spring 可能会将 Model 数据存至不同的地方,对于 JSP 页面来说,Spring MVC 就把 Model 数据存到了 request attribute 中。


Spring MVC 中可以使用 Model 或 Map 类型接收 model 参数,然后将数据存至 model 中即可。示例代码如下。


    @GetMapping("/user/update")
    public Result<?> updateUser(Model model1, Map<String, Object> model2) {
        return null;
    }


在处理器方法执行前,Spring MVC 还会先执行 controller advice bean 和当前 controller 标注了 @ModelAttribute 注解的方法,并将返回值存入 Model 中,由于目前 Model 使用较少,不再加以赘述。


校验相关参数


Spring MVC 中如果想对请求参数进行校验,直接在处理器方法参数上添加 @Validated 注解即可,校验结果则可以定义一个类型为 Errors 的处理器方法参数接收。更多参数校验内容,可以参考我前面文章《Spring 参数校验最佳实践及原理解析》。


这里给出一个简单的示例代码。


@Data
public class User {
    @NotNull(message = "id不能为空")
    private Long id;
    @NotBlank(message = "名称不能为空")
    private String name;
}
@RestController
public class UserController {
    @GetMapping("/user/update")
    public Result<?> updateUser(@RequestBody @Validated User user, Errors errors) {
        if(errors.hasErrors()){
            return Result.fail("参数校验失败");
        }
        return null;
    }
}


表达式参数


Spring MVC 还支持 @Value 标注的注解获取环境变量或属性文件中的属性,可以使用表达式指定默认值或环境变量的名称。示例代码如下。


    @GetMapping("/environment")
    public Result<?> updateUser(@Value("${server.port}") String port) {
        return Result.ok(port);
    }


总结

Spring MVC 处理器方法由于未限制方法签名,因此较为灵活,Spring MVC 默认情况下支持了不同的处理器方法参数类型,有的用于获取请求参数,有的则用于其他功能。


为了支持良好的扩展性,Spring 内部其实是通过组合模式来实现的,Spring 通过 HandlerMethodArgumentResolver 接口解析处理器方法参数,默认的实现及能处理的方法参数我这里做了一个总结,见下图。欢迎留言交流。


27.png

目录
相关文章
|
6天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
30天前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
1月前
|
XML JSON 数据库
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
这篇文章详细介绍了RESTful的概念、实现方式,以及如何在SpringMVC中使用HiddenHttpMethodFilter来处理PUT和DELETE请求,并通过具体代码案例分析了RESTful的使用。
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
|
1月前
|
前端开发 应用服务中间件 数据库
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
这篇文章通过一个具体的项目案例,详细讲解了如何使用SpringMVC、Thymeleaf、Bootstrap以及RESTful风格接口来实现员工信息的增删改查功能。文章提供了项目结构、配置文件、控制器、数据访问对象、实体类和前端页面的完整源码,并展示了实现效果的截图。项目的目的是锻炼使用RESTful风格的接口开发,虽然数据是假数据并未连接数据库,但提供了一个很好的实践机会。文章最后强调了这一章节主要是为了练习RESTful,其他方面暂不考虑。
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
|
1月前
|
JSON 前端开发 Java
Spring MVC返回JSON数据
综上所述,Spring MVC提供了灵活、强大的方式来支持返回JSON数据,从直接使用 `@ResponseBody`及 `@RestController`注解,到通过配置消息转换器和异常处理器,开发人员可以根据具体需求选择合适的实现方式。
87 4
|
1月前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
72 3
|
1月前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
67 2
|
2月前
|
前端开发 Java 应用服务中间件
我以为我对Spring MVC很了解,直到我遇到了...
所有人都知道Spring MVC是是开发的,却鲜有人知道Spring MVC的理论基础来自于1978 年提出MVC模式的一个老头子,他就是Trygve Mikkjel Heyerdahl Reenskaug,挪威计算机科学家,名誉教授。Trygve Reenskaug的MVC架构思想早期用于图形用户界面(GUI) 的软件设计,他对MVC是这样解释的。MVC 被认为是解决用户控制大型复杂数据集问题的通用解决方案。最困难的部分是为不同的架构组件想出好的名字。模型-视图-编辑器是第一个。
108 1
我以为我对Spring MVC很了解,直到我遇到了...
|
2月前
|
前端开发 Java API
Spring Boot 中的 MVC 支持
### Spring Boot 注解摘要 - **@RestController** - **@RequestMapping** - **@PathVariable** - **@RequestParam** - **@RequestBody**
26 2
|
1月前
|
前端开发 Java Spring
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
43 0