@RequestMapping

  • value:用于指定请求的 URL。它和 path属性的作用是一样的。如果只有value一个属性可以省略不写
  • path:用于指定请求的 URL,它和value的属性作用一样,但是不能省略
  • method:用于指定请求的方式
  • params:用于指定限制请求参数的条件
  • hereders:用于指定限制请求消息头的条件

requestmappoing参数

controller:

1
2
3
4
5
6
7
8
9
10
11
@Controller
@RequestMapping(path = "/user")
public class UserController {

@RequestMapping(value = "/select/phone",method = RequestMethod.GET,params = {"phone")
public String selectByPhone(String phone){
//...
System.out.println("测试requestMapping");
return null;
}
}

path 用来指controller的寻址,value用来指方法的寻址,但是他们功能一致

method 用来指定访问方法的请求类型,请求类型不同无法访问。

param 接收数组类型数据,用大括号抱起来,它限制了必须接收到的参数,如果没有这里的参数,就会报错

时间接收

如果你输入的时间格式是 yyyy-mm-dd,那么你大概率会碰到400的报错,那是因为springmvc有自己默认的时间格式 是 yyyy/mm/dd,如果你想要用自己的格式来,那需要自定义转换器。

首先确保你的springmvc.xml 中开启自定义注解

1
<mvc:annotation-driven></mvc:annotation-driven>

然后看例子:

1
2
3
4
5
@RequestMapping(value = "/canshu",params = {"name","money"})
public void show02(String name, @DateTimeFormat(pattern = "yyyy-MM-dd") Date money) {
System.out.println("name:::" + name);
System.out.println("money:::" + money);
}

类似于java的日期类型转换

可以用在参数的声明,也可以直接定义在成员变量上声明

修饰日期类型才会真正有效

还有第二种方法:

1、创建一个转换类

2、在你的springmvc.xml 中添加转换器的位置

3、重新设置指定的自定义注解

转换类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ls.controller;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source != null && !source.trim().equals("")) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
try {
Date date = format.parse(source);
return date;
} catch (ParseException e) {
System.out.println("日期格式错误!");
e.printStackTrace();
}
}
return null;
}
}

增加配置器:

1
2
3
4
5
6
7
8
9
10
<!-- 配置类型转换器工厂 -->
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- 给工厂注入一个新的类型转换器 -->
<!-- 配置自定义类型转换器 -->
<property name="converters">
<array>
<bean class="com.czxy.mvc.converter.StringToDateConverter"></bean>
</array>
</property>
</bean>

修改自定义转化

1
<mvc:annotation-driven conversion-service="converterService"/>

之后接受的日期类型都是你指定的类型,不需要用到@DateTimeFormat注解了

servletAPI

SpringMVC还支持使用原始 ServletAPI对象作为控制器方法的参数。支持原始 ServletAPI对象有:

  • HttpServletRequest (常用)
  • HttpServletResponse (常用)
  • HttpSession (常用)
  • java.security.Principal
  • Locale
  • InputStream
  • OutputStream
  • Reader
  • Writer

我们可以把上述对象,直接写在控制的方法参数中使用。例如需要使用HttpSession获取session中登录的用户信息,可以将HttpSession对象作为方法参数

这个方法直接访问:

1
2
3
4
5
6
7
8
9
10
@RequestMapping("/test/servletAPI")
public String testServetAPI(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);

return "success";
}

当然这些并不是只能用来打印的,我们的return跳转的原始方法可以用 request来实现

1
request.getRequestDispatcher("跳转页面").forward(request,response)

forward和redirect

forward

  1. 转发都是一次请求: A -> B -> C 整个过程还是一次请求,也就是同个request,同一个作用域,过程中的信息共享
  2. 转发地址栏显示地址不变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping("/forward")
public String forward(HttpServletRequest request,
HttpSession session){
request.setAttribute("name","张三");
session.setAttribute("age",20);
System.out.println("参数设置完毕...转发...");
return "forward:/user/get/params.do";
}

@RequestMapping("/get/params")
public String getParams(HttpServletRequest request,
HttpSession session){
String name = (String) request.getAttribute("name");
Integer age = (Integer) session.getAttribute("age");
System.out.println("name : "+name+" age : "+age);
return "success";
}

1、默认就是跳转,重定向需要自己指定

2、请求在一个作用域中,前面的消息,后面也可以拿到

3、也可以通过方法跳转,request.getRequestDispatcher(“跳转页面”).forward(request,response)

redirect

  1. 重定向是2次请求:A -> B ,那么A和B 就是不同request,B无法获取到A 的request中的数据,但是只要没有关闭浏览器,A和B还在一次会话总,A和B可以使用session共享数据
  2. 重定向浏览器地址栏会显示新的请求地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping("/redirect")
public String redirect(HttpServletRequest request,
HttpSession session){
request.setAttribute("name","张三");
session.setAttribute("age",20);
System.out.println("参数设置完毕...转发...");
return "redirect:/user/get/params.do";
}

@RequestMapping("/get/params")
public String getParams(HttpServletRequest request,
HttpSession session){
String name = (String) request.getAttribute("name");
Integer age = (Integer) session.getAttribute("age");
System.out.println("name : "+name+" age : "+age);
return "success";
}

1、重定向不默认,需要自己写单词了

2、两次请求,所以信息不可能共享,想要获取公共信息,需要依靠session了。

3、可以直接用方法跳转:response。sendRedirect(“跳转页面”)

注解方式接收参数

  • RequestBody:用于获取请求体内容。
  • PathVariable:用于绑定 url中的占位符
  • RequestHerder:用于获取请求消息头
  • CookieValue:用于把指定 cookie名称的值传入控制器方法参数

@RequestParam

把请求中指定名称的参数赋值给方法的形参。也就是说不要求你必须把方法参数写成与请求的参数一致了

1
?username=tom  -->  public String hello(@requestParam("username") String name)

还可以添加一个属性,表示这个参数,必须传进来

1
@RequestParam(value = "userToken",required = true) String token

@RequestBody

解析传入进来的json格式数据

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 版本管理 -->
<jackson-version>2.11.0</jackson-version>


<!-- 依赖jar -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-version}</version>
</dependency>
1
2
3
4
5
6
//使用pojo对象接收json串
@RequestMapping("/test/body/user")
public String testBodyPojo(@RequestBody(required = false) User user){
System.out.println(user);
return "success";
}

前端要传入json类型的数据

@PathVeriable

用于绑定 url中的占位符。例如:请求 url中 /delete/**{id},这个{id}**就是 url占位符。

不支持get请求

url支持占位符是 spring3.0之后加入的。是 springmvc支持 rest风格 URL的一个重要标志。

1
2
3
4
5
@RequestMapping(value = "/user/{id}/{name}",method = RequestMethod.POST)
public String testPathVariable(@PathVariable(value = "id",required = true)Integer id,@PathVariable("name")String name){
System.out.println("id = "+id+" name = "+name);
return "success";
}

前端直接用/拼接参数 : /user/12/haoren

@RequestHeader

浏览器传递数据过来的时候,会有请求头,这个就是用来获取请求头的

1
2
3
4
5
6
7
@RequestMapping("/header")
public String getHeader(@RequestHeader(value = "Host")String host,
@RequestHeader(value = "token",required = true)String token){
System.out.println("Host: "+host);
System.out.println("token: "+token);
return "success";
}

1、请求头可以在浏览器按f12看

2、请求头可以用 required = true 来设定是不是必须有

@CookieValue

用于把指定 cookie 名称的值传入控制器方法的参数中。

1
2
3
4
5
@RequestMapping(value = "/cookie/value",method = RequestMethod.GET)
public String getCookieVal(@CookieValue("JSESSIONID")String jsessionID){
System.out.println("JSESSIONID: "+jsessionID);
return "success";
}

1、required:是否必须有此 cookie

springMVC文件上传

文件上传的3要素

  1. form表单的 enctype取值必须是:multipart/form-data

    (默认值是:application/x-www-form-urlencoded)

    enctype:是表单请求正文的类型

  2. method属性取值必须是 Post

  3. 提供一个文件选择域

HTTP协议中新增的rfc1867协议,为 http 协议添加了上传功能。

根据 rfc1867协议规范,enctype=”multipart/form-data”, method=post, type=”file” 。这三个属性是必须的。multipart/form-data 是新增的编码类型,以提高二进制文件的传输效率。

  • enctype属性值

    enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。

描述
application/x-www-form-urlencoded 默认值。在发送前对所有字符进行编码为名称/值对
(将空格转换为 “+” 符号,特殊字符转换为 ASCII HEX 值)。
multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
text/plain 空格转换为 “+” 加号,但不对特殊字符编码。

文件上传流程

  1. 客户端的浏览器,如 Microsoft IE, Mozila Firefox, Chrome 等,按照此规范将用户指定的文件发送到服务器端的网页程序。
  2. 服务器端的网页程序,如 php, asp, jsp 等,可以按照此规范,解析出用户发送来的文件。经过编码发送给服务器。
  3. http server ,例如 tomcat ,已经支持此协议,可接收发送来的文件。

pom:

1
2
3
4
5
6
7
<!--文件上传-->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>

controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 //注意MultipartFile变量名必须和form表单的file的name属性一致
@RequestMapping("/upload")
public String uploadFile(String picname, MultipartFile uploadFile,
HttpServletRequest request) throws IOException {
if(uploadFile == null){
return "error";
}
//保存文件名
String fileName = "";

//1.获取原始文件名
String originalFilename = uploadFile.getOriginalFilename();
//文件扩展名
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//把文件加上随机数,防止重名被覆盖
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//2. 修改文件名,防止文件重名冲突
//文件名 = UUID_输入的文件名.扩展名 / UUID_原文件名.扩展名
//判断是否输入文件名
if(!StringUtils.isEmpty(picname)){
fileName = uuid+"_"+picname+suffix;
}else {
fileName = uuid+"_"+originalFilename;
}
System.out.println(fileName);

//3. 以日期区分保存文件的文件夹
//3.1.获取文件路径
ServletContext servletContext = request.getSession().getServletContext();
String basePath = servletContext.getRealPath("/upload");
//3.2.按日期创建文件夹,避免一个文件夹文件过多
String datePath = new SimpleDateFormat("yyyy-mm-dd").format(new Date());
//3.3.拼接存放文件的文件夹路径
File dir = new File(basePath + "/" + datePath);
//3.4.判断文件夹是否存在,不存在则创建目录
if (!dir.exists()){
dir.mkdir();
}
//4.将上传的文件保存到文件夹
//使用 MulitpartFile 接口中方法,把上传的文件写到指定位置
uploadFile.transferTo(new File(dir,fileName));

return "success";
}

1、判断传进来东西没有

2、获取文件名字

3、获取文件的扩展名

4、判断用户是否传入了名称,传入了则使用,否则默认uuid加扩展名

5、设置上传的位置,主要是生成日期文件夹,防止一个文件夹内文件过多

6、将上传的文件保存到文件夹 用 transferTo 方法

其实上传就两部,起名+存放,但是要考虑各种因素,导致步骤很多

上传注意三点,multipart/form-data, post请求,type=file 并且name与后端形式参数一致

springmvc文件下载

download.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<a href="/download?filename=年轻的我.jpg">年轻的我</a>
</body>
</html>

controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@RequestMapping("/download")
public String download(String filename, HttpServletResponse response){
//没有下载的文件名可能是非法访问,直接返回null
if(StringUtils.isEmpty(filename)){
return null;
}
System.out.println("用户要下载的文件:"+filename);
//服务器存放的源文件
File sourceFile = new File("H:\\upload\\2020-07-27\\5C1CE96586654999BB7C12A18ECE26B0_帅照.jpg");

//数据写入到byte数组
byte[] bytes = null;
FileInputStream inputStream = null;
OutputStream outputStream = null;
try {
//将下载的文件通过输入流读取
inputStream = new FileInputStream(sourceFile);
//创建写出数据的字节数组
bytes = new byte[inputStream.available()];
//将输入流数据转成字节
inputStream.read(bytes);

//下载的响应头header中只支持ASCII,文件名需要进行编码,否则下载不显示文件名
filename = URLEncoder.encode(filename,"utf-8");
//设置文件下载的响应头
response.setHeader("Content-Disposition","attachment;filename="+filename);
outputStream = response.getOutputStream();
//将数据写出
outputStream.write(bytes);
//刷新缓冲区
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件下载异常!");
}finally {
//关闭输入流
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

//关闭输出流
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return null;
}

那个响应头如果不设置,就不会跳出现在弹窗,所以还是设置的好