携手创作,共同成长!这是我参与「掘金日新计划 · 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来构造
//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);
}