Spring MVC之——响应返回值、文件上传、异常处理、拦截器
响应返回值
String类型
controller 方法(SpringMvc的控制器方法)返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
1 | //指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp |
Spring类型之——转发和重定向
- forward 转发:controller 方法在提供了 String 类型的返回值之后,默认就是请求转发。也可以这样
1
2
3
4
5
6
7
8
9/**
* 转发
* @return
*/
public String testForward() {
System.out.println("AccountController 的 testForward 方法执行了。。。。");
return "forward:/WEB-INF/pages/success.jsp";
}需要注意的是,如果用了
formward:
则路径必须写成实际视图 url,不能写逻辑视图。
它相当于“request.getRequestDispatcher("url").forward(request,response)”
。
使用请求转发,既可以转发到 jsp,也可以转发到其他的控制器方法。 - Redirect 重定向:contrller 方法提供了一个 String 类型返回值之后,它需要在返回值里使用:redirect
1
2
3
4
5
6
7
8
9/**
* 重定向
* @return
*/
public String testRedirect() {
System.out.println("AccountController 的 testRedirect 方法执行了。。。。");
return "redirect:testReturnModelAndView";
}它相当于
“response.sendRedirect(url)”
。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不能写在 WEB-INF 目录中,否则无法找到。
void类型
我们知道 Servlet 原始 API 可以作为控制器中方法的参数:
1 | "/testReturnVoid") ( |
在 controller 方法形参上可以定义 request 和 response,使用 request 或 response 指定响应结果:
- 1、使用 request 转向页面
1
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);
- 2、也可以通过 response 页面重定向:
1
response.sendRedirect("testRetrunString")
- 3、也可以通过 response 指定响应结果,例如响应 json 数据:
1
2
3response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json 串");
ModelAndView类型
ModelAndView 是 SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。
示例代码:
1 | /** |
@ResponseBody注解响应json数据
- 作用:该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的
数据如:json,xml 等,通过 Response 响应给客户端 - 注意:Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入
jackson 的包 - maven坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
1 | /** |
文件上传
文件上传的必要前提:
- 1、form 表单的 enctype 取值必须是:
multipart/form-data
(默认值是:application/x-www-form-urlencoded)
- enctype:是表单请求正文的类型
- 2、method 属性取值必须是 Post
- 3、提供一个文件选择域
<input type=”file” />
文件上传的原理分析
当 form 表单的 enctype 取值不是默认值后,**request.getParameter()**将失效。
- 当
enctype=”application/x-www-form-urlencoded”
时,form 表单的正文内容是:key=value&key=value&key=value
- 当 form 表单的 enctype 取值为
Mutilpart/form-data
时,请求正文内容就变成:1
2
3
4
5
6
7
8
9
10每一部分都是 MIME 类型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--
借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和
commons-io。commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它
工作时需要 commons-io 包的支持。
1 | <dependency> |
传统方式的文件上传
传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上。
并且上传完成之后,浏览器可能跳转。
- 前端页面
1
2
3
4
5<h3>文件上传</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form> - 编写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/**
* 文件上传
* @throws Exception
*/
@RequestMapping(value="/fileupload")
public String fileupload(HttpServletRequest request) throws Exception {
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,一会向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径
if(!file.exists()) {
file.mkdirs();
}
// 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request对象
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历
for (FileItem fileItem : list) {
// 判断文件项是普通字段,还是上传的文件
if(fileItem.isFormField()) {
}else {
// 上传文件项
// 获取到上传文件的名称
String filename = fileItem.getName();
// 上传文件
fileItem.write(new File(file, filename));
// 删除临时文件
fileItem.delete();
}
}
return "success";
}
SpringMVC传传统方式的文件上传
SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的
name属性名称相同。
- 前端代码
1
2
3
4
5<form action="/fileUpload" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/><br/>
图片:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上传"/>
</form> - 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/**
* 文件上传的的控制器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Controller("fileUploadController")
public class FileUploadController {
/**
* 文件上传
*/
@RequestMapping("/fileUpload")
public String testResponseJson(String picname,MultipartFileuploadFile,HttpServletRequest request) throws Exception{
//定义文件名
String fileName = "";
//1.获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
//2.截取文件扩展名
String extendName =uploadFileName.substring(uploadFileName.lastIndexOf(".")+1, nuploadFileName.length());
//3.把文件加上随机数,防止文件重复
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//4.判断是否输入了文件名
if(!StringUtils.isEmpty(picname)) {
fileName = uuid+"_" + picname +"." + extendName;
}else {
fileName = uuid+"_"+uploadFileName;
}
System.out.println(fileName);
//2.获取文件路径
ServletContext context = request.getServletContext();
String basePath = context.getRealPath("/uploads");
//3.解决同一文件夹中文件过多问题
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
//4.判断路径是否存在,不存在创建
File file = new File(basePath+"/"+datePath);
if(!file.exists()) {
file.mkdirs();
}
//5.使用 MulitpartFile 接口中方法,把上传的文件写到指定位置
uploadFile.transferTo(new File(file,fileName));
return "success";
}
} - 配置文件解析器
1
2
3
4
5
6
7
8
9<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" <!-- id 的值是固定的-->
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为 5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
注意:
文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他
字段也将无法绑定)
SpringMVC跨服务器方式文件上传
在实际开发中,我们会有很多处理不同功能的服务器。例如:
- 应用服务器:负责部署我们的应用
- 数据库服务器:运行我们的数据库
- 缓存和消息服务器:负责处理大并发访问的缓存和消息
- 文件服务器:负责存储用户上传文件的服务器。
分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
1、准备两个 tomcat 服务器,并创建一个用于存放图片的 web 工程
2. 实现SpringMVC跨服务器方式文件上传
- 前端代码:
1
2
3
4
5<h3>跨服务器的文件上传</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form> - 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/**
* SpringMVC跨服务器方式的文件上传
*
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("SpringMVC跨服务器方式的文件上传...");
// 定义图片服务器的请求路径
String path = "http://localhost:9090/day02_springmvc5_02image/uploads/";
// 获取到上传文件的名称
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名称唯一化
filename = uuid+"_"+filename;
// 向图片服务器上传文件
// 创建客户端对象
Client client = Client.create();
// 连接图片服务器
WebResource webResource = client.resource(path+filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
} - 配置解析器
1
2
3
4
5
6
7
8<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为 5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
SpringMVC 中的异常处理
异常处理的思路
系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,
后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端
控制器交由异常处理器进行异常处理,如下图:
实现异常处理
- 自定义异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* 自定义异常
*/
public class CustomException extends Exception {
private String message;
public CustomException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
} - 自定义异常处理器
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/**
* 自定义异常处理器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class CustomExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
CustomException customException = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
customException = new CustomException("系统错误,请与系统管理 员联系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
} - 配置异常处理器
1
2<!-- 配置自定义异常处理器 -->
<bean id="handlerExceptionResolver" class="com.itheima.exception.CustomExceptionResolver"/>
SpringMVC 中的拦截器
- SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。
- 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
- 拦截器和过滤器的功能比较类似,有区别
- 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。
- 拦截器是SpringMVC框架独有的。
- 过滤器配置了
/*
,可以拦截任何资源。 - 拦截器只会对控制器中的方法进行拦截。
- 拦截器也是AOP思想的一种实现方式
- 想要自定义拦截器,需要实现HandlerInterceptor接口。
实现自定义拦截器
- 创建类,实现HandlerInterceptor接口,重写需要的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package cn.itcast.demo1;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 自定义拦截器1
* @author rt
*/
public class MyInterceptor1 implements HandlerInterceptor{
/**
* controller方法执行前,进行拦截的方法
* return true放行
* return false拦截
* 可以使用转发或者重定向直接跳转到指定的页面。
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("拦截器执行了...");
return true;
}
} - 在springmvc.xml中配置拦截器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/user/*"/>
<!-- 哪些方法不进行拦截
<mvc:exclude-mapping path=""/>
-->
<!-- 注册拦截器对象 -->
<bean class="cn.itcast.demo1.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
HandlerInterceptor接口中的方法
- preHandle方法是controller方法执行前拦截的方法
- 可以使用request或者response跳转到指定的页面
- return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
- return false不放行,不会执行controller中的方法。
- postHandle是controller方法执行后执行的方法,在JSP视图执行前。
- 可以使用request或者response跳转到指定的页面
- 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
- postHandle方法是在JSP执行后执行
- request或者response不能再跳转页面了
配置多个拦截器
1 | <!-- 配置拦截器 --> |
ps:拦截器执行顺序按照配置的前后决定