响应数据和结果视图
- 字符串
controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
//指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp
@RequestMapping("/testReturnString")
public String testReturnString() {
System.out.println("AccountController 的 testReturnString 方法执行了。。。。");
return "success"; }
- void
Servlet 原始 API 可以作为控制器中方法的参数:
在 controller 方法形参上可以定义 request 和 response,使用 request 或 response 指定响应结果:
@RequestMapping("/testReturnVoid")
public void testReturnVoid(HttpServletRequest request,HttpServletResponse response)
throws Exception {
}
- 使用 request 转向页面,如下:
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,
response);
- 也可以通过response页面重定向
response.sendRedirect("testReturnString");
- 也可以通过 response 指定响应结果,例如响应 json 数据:
response.setCharacterEncoding(“utf-8”);
response.setContentType(“application/json:charset=utf-8”);
reponse.getWriter().write(“json串”);
- ModelAndView
ModelAndView是SpringMvc为我们提供的一个对象,该对象也可以用作控制器的返回值
该对象有两个方法
public ModelAndView addObject(String attributeName,Object attributeValue){
getMap().addAttribute(attributeName,attributeValue);
return this;
}
//我们可以使用el表达式在页面中获取attributeValue: ${attributeName}
public void setViewNME(@Nullable String ViewNAme){
this.view = viewName;
}
//用于设置逻辑视图名称,视图解析器会根据名称前往指定的视图
示例代码
/**
* 返回 ModeAndView
* @return
*/
@RequestMapping("/testReturnModelAndView")
public ModelAndView testReturnModelAndView() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "张三");
mv.setViewName("success");
retrun mv;
响应的 jsp 代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>执行成功</title>
</head> <body>
执行成功!
${requestScope.username}
</body>
</html>
注意:
我们在页面上获取使用的是 requestScope.username 取的,所以返回 ModelAndView 类型时,浏
览器跳转只能是请求转发。
- 转发和重定向
- forward 转发
controller 方法在提供了 String 类型的返回值之后,默认就是请求转发。我们也可以写成:
/**
- 转发
- @return
*/
@RequestMapping("/testForward")
public String testForward() {
System.out.println(“AccountController 的 testForward 方法执行了。。。。”);
return “forward:/WEB-INF/pages/success.jsp”; }
需要注意的是,如果用了 formward:则路径必须写成实际视图 url,不能写逻辑视图。
它相当于“request.getRequestDispatcher(“url”).forward(request,response)”。使用请求
转发,既可以转发到 jsp,也可以转发到其他的控制器方法。
2. Redirect 重定向
```
contrller 方法提供了一个 String 类型返回值之后,它需要在返回值里使用:redirect:
/**
* 重定向
* @return
*/
@RequestMapping("/testRedirect")
public String testRedirect() {
System.out.println("AccountController 的 testRedirect 方法执行了。。。。");
return "redirect:testReturnModelAndView"; }它相当于“response.sendRedirect(url)”。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不
能写在 WEB-INF 目录中,否则无法找到。
ResponseBody 响应 json 数据
作用:
用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
get 请求方式不适用。
屬性
required:是否必须有请求体。默认值是:true。
当请求body为空,取值为 true 时,get 请求方式会报错。如果取值
为 false,get 请求得到是 null
jsp 中的代码:
<script type="text/javascript"
src="${pageContext.request.contextPath}/js/jquery.min.js"></script> <script type="text/javascript">
$(function(){
$("#testJson").click(function(){
$.ajax({
type:"post",
url:"${pageContext.request.contextPath}/testResponseJson",
contentType:"application/json;charset=utf-8",
data:'{"id":1,"name":"test","money":999.0}',
dataType:"json",
success:function(data){
alert(data);
}
});
});
})
</script>
<!-- 测试异步请求 --> <input type="button" value="测试 ajax 请求 json 和响应 json" id="testJson"/>
控制器中的代码:
@Controller("jsonController")
public class JsonController {
/**
* 测试响应 json 数据
*/
@RequestMapping("/testResponseJson")
public @ResponseBody Account testResponseJson(@RequestBody Account account) {
System.out.println("异步请求:"+account);
return account; } }
文件上传
步骤
- 导入文件上传jar包
- 编写文件上传的jsp页面
- 编写文件上传的controller
文件上传的必要前提
- form 表单的 enctype 取值必须是:multipart/form-data
(默认值是:application/x-www-form-urlencoded)
enctype:是表单请求正文的类型- method 属性取值必须是 Post
- 提供一个文件选择域
借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和
commons-io。commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它
工作时需要 commons-io 包的支持。
springmvc 传统方式的文件上传
- 说明
传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上。
并且上传完成之后,浏览器可能跳转。
实现步骤
- 拷贝文件上传的 jar 包到工程的 lib 目录
- 编写 jsp 页面
<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>
- 编写控制器
/**
* 文件上传的的控制器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Controller("fileUploadController")
public class FileUploadController {
/**
* 文件上传
*/
@RequestMapping("/fileUpload")
public String testResponseJson(String picname,MultipartFile
uploadFile,HttpServletRequest request) throws Exception{
//定义文件名
String fileName = "";
//1.获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
//2.截取文件扩展名
String extendName =
uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,
uploadFileName.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"; } }
- 配置文件解析器
<!-- 配置文件上传解析器 --> <bean id="multipartResolver" <!-- id 的值是固定的-->
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为 5MB --> <property name="maxUploadSize"> <value>5242880</value>
</property>
</bean>
注意
文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他
字段也将无法绑定)
springmvc 跨服务器方式的文件上传
- 分服务器的目的
在实际开发中,我们会有很多处理不同功能的服务器。例如:
应用服务器:负责部署我们的应用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
(注意:此处说的不是服务器集群)
步骤
- 准备两个 tomcat 服务器,并创建一个用于存放图片的 web 工程
- 拷贝 jar 包
- 编写控制器实现上传图片
/**
* 响应 json 数据的控制器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Controller("fileUploadController2")
public class FileUploadController2 {
public static final String FILESERVERURL =
"http://localhost:9090/day06_spring_image/uploads/";
/**
* 文件上传,保存文件到不同服务器
*/
@RequestMapping("/fileUpload2")
public String testResponseJson(String picname,MultipartFile uploadFile) throws Exception{
//定义文件名
String fileName = "";
//1.获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
//2.截取文件扩展名
String extendName =
uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,
uploadFileName.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);
//5.创建 sun 公司提供的 jersey 包中的 Client 对象
Client client = Client.create();
//6.指定上传文件的地址,该地址是 web 路径
WebResource resource = client.resource(FILESERVERURL+fileName);
//7.实现上传
String result = resource.put(String.class,uploadFile.getBytes());
System.out.println(result);
return "success"; } }
- 编写 jsp 页面
<form action="fileUpload2" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/><br/>
图片:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上传"/>
</form>
- 配置解析器
<!-- 配置文件上传解析器 --> <bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为 5MB -->
<property name="maxUploadSize"> <value>5242880</value>
</property>
</bean>
SpringMVC 中的异常处理
- 思路:逐级往上抛,最终由DispatcherServlet找异常处理器进行异常处理
实现步骤
- 编写异常类和错误页面
public class CustomException extends Exception {
private String message;
public CustomException(String message) {
this.message = message; }
public String getMessage() {
return message; } }
jsp 页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>执行失败</title>
</head> <body>
执行失败!
${message }
</body>
</html>
- 自定义异常处理器
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
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; } }
- 配置异常处理器
<!-- 配置自定义异常处理器 --> <bean id="handlerExceptionResolver"
class="com.itheima.exception.CustomExceptionResolver"/>
拦截器
概述
1.Springmvc框架中的拦截器用于对处理器进行预处理和后处理技术
2.可以定义拦截链
3.拦截器和过滤器的区别
-
过滤器是servlet规范中的一部分
-
拦截器是springmvc独有的
-
过滤器配置了/*可以拦截任何资源
-
拦截器只会对控制器中的方法进行拦截
自定义拦截器的步骤
- 第一步:编写一个普通类实现 HandlerInterceptor 接口
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler)
throws Exception {
System.out.println("preHandle 拦截器拦截了");
return true; }
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler,ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion 方法执行了");
} }
- 第二步:配置拦截器
<!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/>
<bean id="handlerInterceptorDemo1"
class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
</mvc:interceptors>
拦截器的细节
- 拦截器的放行
放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器
中的方法。
拦截器中方法的说明
public interface HandlerInterceptor {
/**
* 如何调用:
* 按拦截器定义顺序调用
* 何时调用:
* 只要配置了都会调用
* 有什么用:
* 如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去
* 进行处理,则返回 true。 * 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。 */
default boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler)
throws Exception {
return true;
}
/**
* 如何调用:
* 按拦截器定义逆序调用
* 何时调用:
* 在拦截器链内所有拦截器返成功调用
* 有什么用:
* 在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,
* 在该方法中对用户请求 request 进行处理。
*/
default void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 如何调用:
* 按拦截器定义逆序调用
* 何时调用:
* 只有 preHandle 返回 true 才调用
* 有什么用: * 在 DispatcherServlet 完全处理完请求后被调用,
* 可以在该方法中进行一些资源清理的操作。
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler,
@Nullable Exception ex) throws Exception {
} }
思考:
如果有多个拦截器,这时拦截器 1 的 preHandle 方法返回 true,但是拦截器 2 的 preHandle 方法返
回 false,而此时拦截器 1 的 afterCompletion 方法是否执行?
拦截器的作用路径
作用路径可以通过在配置文件中配置。
<!-- 配置拦截器的作用范围 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /><!-- 用于指定对拦截的 url --> <mvc:exclude-mapping path=""/><!-- 用于指定排除的 url--> <bean id="handlerInterceptorDemo1"
class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器的执行顺序
多个拦截器是按照配置的顺序决定的。
拦截器的简单案例(验证用户是否登录)
实现思路
- 有一个登录页面,需要写一个 controller 访问页面
- 登录页面有一提交表单的动作。需要在 controller 中处理。
- 判断用户名密码是否正确
- 如果正确 向 session 中写入用户信息
- 返回登录成功。
- 拦截用户请求,判断用户是否登录
- 如果用户已经登录。放行
- 如果用户未登录,跳转到登录页面
- 控制器代码
//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
return "login"; }
//登陆提交
//userid:用户账号,pwd:密码
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session,String userid,String pwd)throws
Exception{
//向 session 记录用户身份信息
session.setAttribute("activeUser", userid);
return "redirect:/main.jsp"; }
//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session 过期
session.invalidate();
return "redirect:index.jsp"; }
- 拦截器代码
public class LoginInterceptor implements HandlerInterceptor{
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//如果是登录页面则放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true; }
HttpSession session = request.getSession();
//如果用户已登录也放行
if(session.getAttribute("user")!=null){
return true; }
//用户没有登录挑战到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
response);
return false; } }
preHandle:在controller方法执行前执行
- 可以使用request或者response跳转到指定的页面
- return true 放行,执行下一个拦截器,如果没有拉姆节气,执行controller方法
- return false不放行,不会执行controller里的方法
postHandler是controller方法执行后执行的方法,在jsp试图执行前
- 可以使用request或者response跳转到指定的页面
- 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
afterComplication:在jsp视图执行后执行
request或者response不能再跳转页面了