Handler方法形参1

109 阅读7分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

★请求参数的封装

request.getParameter

在HandlerMethod的形参中直接接收到这些请求参数

请求参数名和Handler方法的形参名一致

接收为什么类型的形参?

  • 直接接收为String类型的形参
  • 基本类型以及对应的包装类 → 本来直接接收到的是String(request.getParameter),使用了SpringMVC提供的类型转换器Converter
  • Date → 提供了Converter
  • 数组 → request.getParameters
  • 文件(MultipartFile) → 文件上传 → 1行代码就能搞定

String、基本类型、包装类

接收基本类型的值的时候,而我们提供的请求参数,没有提供他的请求参数,发生了500

建议: 能够使用基本类型或包装类的时候,尽量去使用包装类,增加你应用程序的健壮性

 // 字符串和包装类
 //localhost:8080/user/register1?username=songge&password=niupi&age=40
 @RequestMapping("register1")
 public BaseRespVo register1(String username,String password,Integer age) {
     return BaseRespVo.ok("register1:" + username + "-->" + password + "-->" + age);
 }
 ​
 // 字符串和基本类型
 //localhost:8080/user/register2?username=songge&password=niupi
 @RequestMapping("register2")
 public BaseRespVo register2(String username,String password,int age) {
     return BaseRespVo.ok("register2" + username + "-->" + password + "-->" + age);
 }

Date日期类型

Date:可以直接接收,但是需要指定格式

直接接收意味着提供了Converter,String → Date,默认的日期格式是yyyy/MM/dd

如果请求参数的值并不是按照默认的日期格式来提供,也是可以接收到的,但是需要你来指定日期的格式@DateTimeFormat,pattern属性用来指定日期的格式

 //localhost:8080/user/register3?username=songge&password=niupi&age=30
 // &birthday=2022/06/21
 @RequestMapping("register3")
 public BaseRespVo register3(String username, String password, Integer age,
                             Date birthday) {
     return BaseRespVo.ok("register3:" + username + "-->" + password + "-->" + age);
 }
 ​
 ​
 //localhost:8080/user/register4?username=songge&password=niupi&age=30
 // &birthday=2022-06-21
 @RequestMapping("register4")
 public BaseRespVo register4(String username, String password, Integer age,
                             @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
     return BaseRespVo.ok("register4:" + username + "-->" + password + "-->" + age);
 }

数组格式

数组: request.getParameters,接收数组的

主要是前端要按照特定的格式来构造请求参数吗,多个相同的请求参数

 localhost:8080/user/register5?username=songge&password=niupi&age=30
 &hobbies=sing&hobbies=dance&hobbies=rap
 &ids=1&ids=2&ids=3

使用String数组来接收hobbies,使用Integer数组(也是因为Converter起作用)来接收ids

 //localhost:8080/user/register5?username=songge&password=niupi&age=30
 // &hobbies=sing&hobbies=dance&hobbies=rap
 // &ids=1&ids=2&ids=3
 @RequestMapping("register5")
 public BaseRespVo register5(String username, String password, Integer age,
                             String[] hobbies,
                             Integer[] ids) {
     return BaseRespVo.ok("register5:" + hobbies + "-->" + ids);
 }

引用类型对象

引用类型对象: :接收的是前面我们直接接收过的请求参数

原先直接写在Handler方法形参中的这些值,作为引用类型的成员变量

封装过程会用到setter方法

应用类型的对象的名称叫啥不重要,引用类型中的成员变量名(set方法名)要和请求参数名一致

 // 前面所有的请求参数都发送了,接收请求参数的方式做了一些变化
 // 采用引用类型的对象来接收请求参数
 //localhost:8080/user/register6?username=songge&password=niupi&age=30
 // &hobbies=sing&hobbies=dance&hobbies=rap
 // &ids=1&ids=2&ids=3
 // &birthday=2022-06-21
 @RequestMapping("register6")
 public BaseRespVo register6(User user) { //应用类型的对象的名称叫啥不重要
     return BaseRespVo.ok("register6:" + user);
 }
 /*@RequestMapping("register6")
 public BaseRespVo register6(String username, String password, Integer age,
                             String[] hobbies,
                             Integer[] ids,
                             @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
     return BaseRespVo.ok("register6:" + hobbies + "-->" + ids);
 }*/// 前面所有的请求参数都发送了,接收请求参数的方式做了一些变化
     // 采用引用类型的对象来接收请求参数
     //localhost:8080/user/register6?username=songge&password=niupi&age=30
     // &hobbies=sing&hobbies=dance&hobbies=rap
     // &ids=1&ids=2&ids=3
     // &birthday=2022-06-21
     @RequestMapping("register6")
     public BaseRespVo register6(User user) { //应用类型的对象的名称叫啥不重要
         return BaseRespVo.ok("register6:" + user);
     }
     /*@RequestMapping("register6")
     public BaseRespVo register6(String username, String password, Integer age,
                                 String[] hobbies,
                                 Integer[] ids,
                                 @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
         return BaseRespVo.ok("register6:" + hobbies + "-->" + ids);
     }*/

组合方式

思考: 开发过程中我们到底是直接接收还是采用引用类型的对象来接收呢?

全都要。

  • 直接接收:请求参数比较少;用得少
  • 引用类型对象:参数比较多;用的多
  • 也可以混着用,一部分直接接收,一部分用引用类型的对象来接收
 localhost:8080/user/query?age=20&page=2&limit=10
 localhost:8080/order/query?page=1&limit=10
 localhost:8080/goods/query?page=2&limit=10
 localhost:8080/category/query?page=3&limit=5

上面的请求URL中都包含了page和limit这两个请求参数,这两个请求参数常用,就可以使用引用类型对象来封装

 @Data
 public class QueryBo {
     Integer age;
     Integer page;
     Integer limit;
 }
 @Data
 public class QueryBo2 extends PageInfo{
     //封装参数仍然要使用set方法 → 子类中可以使用父类的方法(set)
     Integer age;
 }
 //localhost:8080/user/query?age=20&page=2&limit=10
 @RequestMapping("query")
 public BaseRespVo query(Integer age, PageInfo pageInfo) {
     return BaseRespVo.ok("成功", pageInfo);
 }
 ​
 // 就上面这个请求,我能否用一个引用类型的对象来接收呢
 @RequestMapping("query1")
 public BaseRespVo query1(QueryBo queryBo) {
     return BaseRespVo.ok("成功", queryBo);
 }
 // 就上面这个请求,我能否 再 换一个引用类型的对象来接收呢
 @RequestMapping("query2")
 public BaseRespVo query2(QueryBo2 queryBo2) {
     return BaseRespVo.ok("成功", queryBo2);
 }

如果有多个相同的请求参数被多个方法使用,你可以封装为引用类型的对象,比如PageInfo

而如果你的请求除了这个请求参数(PageInfo),还有其他的多个常用的请求参数,可以使用继承的方式来使用

MultipartFile格式

文件: 特殊的文件接收格式可以直接接收,MultipartFile

通常接收到文件是为了将文件保存到本地,MultipartFile提供了一个方法transferTo可以直接保存到本地

构造表单能够发送文件请求:form表单中的input标签的name属性其实就是请求参数名 → 形参名

 <form action="upload/file" enctype="multipart/form-data" method="post">
     <input type="file" name="myfile"/><br>
     <input type="submit"/>
 </form>

在SpringMVC应用中,如果想要使用这个表单,你要放到jsp中

也可以使用Postman来构造

image.png

 //localhost:8080/upload/file
 @RequestMapping("file")
 public BaseRespVo uploadFile(MultipartFile myfile) {
     return BaseRespVo.ok(null);
 }

还需要做一点儿事情:

  • 引入依赖 commons-io\commons-fileupload
  • 注册组件:multipartResolver → 注意组件id必须为这个值,不能修改为其他值
 <dependency>
     <groupId>commons-fileupload</groupId>
     <artifactId>commons-fileupload</artifactId>
     <version>1.4</version>
 </dependency>
 @Bean("multipartResolver")
 public CommonsMultipartResolver multipartResolver() {
     return new CommonsMultipartResolver();
 }

组件id必须为固定值的原因:doDispatch中使用HandlerAdapter做参数封装的时候会使用multipartResolver组件,组件是从容器中来的,通过组件id来获得这个组件,代码获得组件(id)的代码是已经写好的

 public class DispatcherServlet extends FrameworkServlet {
     public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
 }
 //localhost:8080/upload/file
 @RequestMapping("file")
 public BaseRespVo uploadFile(MultipartFile myfile) throws IOException {
     //transferTo → 需要提供File这样的形参 → 指定保存文件的位置
     // 比如我要上传到d://tmp目录,并且文件名叫bingbing.jpg
     //myfile.transferTo(new File("d://tmp/bingbing.jpg"));
     //myfile.transferTo(new File("d://tmp/","bingbing.jpg"));
 ​
     // 如果我们构造的File,文件名相同,会导致覆盖
     // 可以起不同的名字的File:  1、生成随机值; 2、获得原始的文件名(也是有可能重复的)
 ​
     // 可以获得一些其他的信息 → MultipartFile提供的方法
     String originalFilename = myfile.getOriginalFilename(); //原始文件名
     String random = UUID.randomUUID().toString().replaceAll("-","");
 ​
     String name = myfile.getName(); // 获得请求参数名 → myfile
 ​
     String contentType = myfile.getContentType(); // 获得正文类型 image/jpeg → 文件的类型类型
 ​
     long size = myfile.getSize(); // 获得文件的大小
 ​
     // dlrb.jpg → ["drlb","jpg"]
     String[] split = originalFilename.split("\.");
     String fileName = random + "." + split[split.length - 1];
 ​
     // 也可以这样子去做拆分,找到最后一个点的位置,截取最后一点到最后位置的字符串 → 留给你们实现
 ​
     File file = new File("d://tmp/",fileName);
     myfile.transferTo(file);
 ​
     return BaseRespVo.ok(null);
 }

要上传多个文件input标签中增加multiple

 <h1>多个文件上传</h1>
 <form action="upload/files" enctype="multipart/form-data" method="post">
     <input type="file" multiple name="myfiles"/><br>
     <input type="submit"/>
 </form>
 //localhost:8080/upload/files
 // 接收多个文件 → MultipartFile数组
 @RequestMapping("files")
 public BaseRespVo uploadFiles(MultipartFile[] myfiles) throws IOException {
     for (MultipartFile myfile : myfiles) {
 ​
         String originalFilename = myfile.getOriginalFilename(); //原始文件名
         String random = UUID.randomUUID().toString().replaceAll("-", "");
 ​
         String[] split = originalFilename.split("\.");
         if (split.length <= 1) {
             //ctrl + alt + L → 并不建议用
             // 调整的是当前文件的全部格式
         }
         String fileName = random + "." + split[split.length - 1];
 ​
         // 也可以这样子去做拆分,找到最后一个点的位置,截取最后一点到最后位置的字符串 → 留给你们实现
 ​
         File file = new File("d://tmp/", fileName);
 ​
 ​
         myfile.transferTo(file);
     }
     User data = getUser();
     return BaseRespVo.ok(null, data);
 }