第1章 响应数据和结果视图
1.1 返回值分类
1.1.1 返回字符串
什么情况下用这种方式:(以后的开发基本上都是这样的开发方式)
开发思路:你发请求,我后台把数据查出来,我存到这个Model对象,它帮我存到request转发到页面,前端jsp从request域里面把值取出来。(以后即可用Model这个对象代替HttpServletRequest对象,达到简写的目的,再也不用像HttpServletRequest那么麻烦了...且使用Servlet原生API会时程序耦合很高,具体原因见:SpringMC 总结 01)
@RequestMapping("/testString")
public String testString(Model model) {
System.out.println("execute testString()...");
//模拟从数据库中查询出User对象
User u = new User();
u.setUsername("JTL");
u.setPassword("123");
u.setAge(17);
model.addAttribute("user", u);
return "success";
}
前端界面:通过EL表达式从request域对象中取值
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>执行成功</h3>
${user.username}
${user.password}
${user.age}
</body>
</html>
1.1.2 返回值是void
@RequestMapping("/testVoid")
public void testVoid(Model model) {
System.out.println("execute testVoid()...");
}
默认情况它会去找这个路径下的与你请求路径同名的jsp文件:这样非常不好,因为你又的创建一个名为testVoid.jsp。(默认值)
正确方式:
p.s.区别:
请求转发:请求转发是一次请求,不用编写项目的名称。
重定向:重定向是两次请求,需要编写项目的名称。
注意:
1、你自己手动去调转发的方法,它不会再帮你去执行视图解析器。也就不会自动跳转到/WEB-INF目录下(自己配置的)去找jsp。需要你自己提供完整的目录,不会再使用视图解析器的对象(/称为组件)。
2、当你转发层写完,如果后面还有代码的话它会继续执行,如果你不想让后面代码执行,可手动加一个return;
方式1:请求转发
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("execute testVoid()...");
//编写请求转发的程序
req.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(req, resp);
return;
}
方式2:重定向
1、通过request.getContextPath()获取到项目名称
2、重定向等于又发了一个新的请求,你直接发请求是不能直接请求/WEB-INF/pages里面的页面,/WEB-INF里面的东西不能直接请求,转发是可以的。你只能请求到/webapp根目录下的jsp如index.jsp
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("execute testVoid()...");
//重定向
resp.sendRedirect(req.getContextPath()+"/index.jsp");
return;
}
方式3:直接会进行响应
说明:上面两种方式都是先跳到某个jsp(跳页面),最终由Tomcat服务器帮你生成html,最终帮你响应给用户。还有这样一种情况:你可能直接发请求,控制台/控制器直接通过输出流,把数据响应给浏览器。response.getWrite()拿到输出流。
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("execute testVoid()...");
// 解决响应中文乱码
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//直接会进行响应
resp.getWriter().print("你好!");
return;
}
1.1.3 返回值是ModelAndView对象
说明:由SpringMVC框架提供的一个对象。它也可以通过视图解析器帮你跳转到某个页面。与返回字符串那个方式的功能是一样的,查到一个JavaBean放到Model中然后返回给View视图;ModelAndView也可以存储JavaBean对象,也可存储你想往哪个页面作 跳转,与前面那个代码做的功能是一样的,只是写法有点不一样。且它底层也会把user对象存入到request域对象中,因为ModelAndView源码中就有ModelMap这个属性Model这个接口的实现类里就有:
特别注意:这种方式和反回字符串方式没什么区别,写法有点不一样而已。其实返回字符串这个方式,它的底层最终也会选择ModelAndView这个方式!底层源码还是用的ModelAndView这个类。
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
System.out.println("execute testModelAndView()...");
//创建ModelAndView对象
ModelAndView mv = new ModelAndView();
//模拟从数据库中查询出User对象
User u = new User();
u.setUsername("JT.L");
u.setPassword("123");
u.setAge(17);
// 把user对象存储到mv对象中,它底层也会把user对象存入到request域对象中
mv.addObject("user", u);
// 跳转到哪个页面
mv.setViewName("success");
return mv;
}
1.2 转发和重定向
还有一种写法(SpringMVC框架提供的转发和重定向),用一些关键字来表示转发和重定向,这个方式用的比较少,且当用关键字去做转发或重定向时,它是用不了视图解析器这个对象(组件)的。
1.2.1 forward请求转发
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("execute testForwardOrRedirect()...");
// 请求的转发
return "forward:/WEB-INF/pages/success.jsp";
}
说明:Controller方法在提供了String类型的返回值之后,默认就是请求转发。我们也可以像上面例子,使用forward关键字显式进行请求转发。但是,一旦用了forward:则路径必须写成实际视图url,不能写逻辑视图,因为它用不了视图解析器。它相当于requestl.getRequestDispathcher("url").forward(request,response)。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。
补充,转发到其他控制器:
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("execute testForwardOrRedirect()...");
// 请求的转发
return "forward:testModelAndView";
}
说明:必须加上forward关键字,虽然Controller方法在提供了String类型的返回值之后,默认就是请求转发,但是你不加forward直接写其他控制器的RequestMapping如:testModelAndView是转发不成功的。
1.2.2 Redirect 重定向
说明:使用关键字的重定向,不需要像response.sendRedirect(url)方式通过request.getContextPath()加上项目名称。因为它底层帮你把项目名加上了,以后在用关键字作重定向的时候,不用再去加项目的名称,框架已经默认帮你加好了。
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("execute testForwardOrRedirect()...");
// 重定向
return "redirect:/index.jsp";
}
重定向也可以定向到其他控制器的方法:
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("execute testForwardOrRedirect()...");
// 重定向到其他控制器方法
return "redirect:testModelAndView";
}
总结对比:
1、关键字+String返回类型:请求转发或重定向到jsp或其他控制器方法的对比
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("execute testForwardOrRedirect()...");
// 请求的转发
// return "forward:/WEB-INF/pages/success.jsp";
// 请求转发到其他控制器的方法
// return "forward:testModelAndView";
// 重定向
// return "redirect:/index.jsp";
// 重定向到其他控制器方法
return "redirect:testModelAndView";
}
2、返回值void类型:另一种方式请求转发或重定向到其他控制器方法:
(一)请求转发:
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("execute testVoid()...");
// 请求转发
req.getRequestDispatcher("testModelAndView").forward(req,resp);
return;
}
(二)重定向:
说明:重定向其他控制器方法也不需要写项目名称,但是到其他jsp一定要加上项目名称:
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("execute testVoid()...");
// 重定向 注意这里也不需要写项目名称
resp.sendRedirect("testModelAndView");
return;
}
1.3 ResponseBody响应json数据
说明:之前都是用的转发或者重定向跳转到jsp再去作响应,可能有这样一个场景:页面发送一个ajax请求,明显是一个异步请求,那我的后台需要把一些对象转换成json的字符串,然后给你响应回去。如果有这样一个需求,那么用ResponseBody这个注解就可以完成这个事。
1、先搭建一个异步的环境。
1)引入jquery.min.js
2)存在的问题:在web.xml中配置了前端控制器,拦截了:/,意思就是任何资源都会被拦截到,在jsp中引入js文件其实它也会去请求服务器中对应的那个js文件,现在就存在这样一个问题:DispatcherServlet这个前端控制器会把这些静态的资源文件给拦截了。解决方式:告诉前端控制器这些静态资源(静态资源:js、css、图片)不要去拦截;在springmvc.xml文件中配置:
请求js文件截图:(被拦截了)
springmvc.xml中的配置:
<!--告诉前端控制器,哪些静态资源不拦截-->
<mvc:resources location="/js/" mapping="/js/**"/>
说明:location="/js/" -- js文件夹下面的任何文件都可以不拦截
mapping="/js/**" -- 跟你的映射请求是有关的,以后你请求路径带/js下面任何文件都会不会对它进行拦截,如:
在jsp中的请求:
<script src="js/jquery.min.js"></script>
在浏览器中的表现:
补充其他的:
<!-- 设置静态资源不过滤 -->
<mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
特别注意:
jsp中js路径的两种写法:
1)相对路径方式
<script src="js/jquery.min.js"></script>
2)这种写法记得把isELIgnored="false"设置上,因为用了EL表达式的$符号
<script src="${pageContext.request.contextPath}/js/jquery.min.js">
2、编写ajax请求
<script>
// 页面加载,绑定单击事件
$(function () {
//#btn id选择器
$("#btn").click(function () {
// alert("hello btn");
// 发送ajax请求
$.ajax({
// 编写json格式,设置属性和值
url:"user/testAjax",
//json的maime类型???
contentType:"application/json;charset=UTF-8",
// ''可以往里面再做嵌套 json键值对
data:'{"username":"JTL","password":"123","age":"17"}',
dataType:"json",
type:"post",
success:function (data) {
// data服务器端响应的json数据,进行解析
alert(data);// 会弹出一个对象,因为返回的是一个json的对象
// 解析这个对象 取属性
alert(data.username);
alert(data.password);
alert(data.age);
}
})
})
})
</script>
3、把发过来的json封装到一个JavaBean的对象当中
说明:这个事非常好做,SpringMVC的框架已经帮我们做好了。你发过来的是一串Json的字符串,我能拿到,如果你发的串的key值跟我JavaBean里面的属性名是相同的,那么框架可以帮你把串直接封装到对象当中。但是在做这个转换的时候,把这个key封装到对象的时候,需要用到以下Jar包。这个Jar的作用:把串转成对象;或者将对象转换成Json字符串。
<!-- json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包 -->
<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>
有了以上的Jar包之后,后端是直接能把前端传过来的Json数据封装到JavaBean对象当中去,它是一个自动的过程!只需要你加一个@RequestBody的注解即可。
@RequestMapping("/testAjax")
public void testAjax(@RequestBody User user) {
System.out.println("execute testAjax()...");
// 客户端发送ajax的请求,传的是json字符串,后端(自动)把json字符串封装到user对象中了
System.out.println(user);
}
控制台输出结果:
进一步编写Controller方法:
说明:
@RequestBody:接收的
@ResponseBody:响应的,返回(return user)的时候直接讲user对象转成一个json的字符串,转完了直接帮你响应
@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user) {
System.out.println("execute testAjax()...");
// 客户端发送ajax的请求,传的是json字符串,后端(自动)把json字符串封装到user对象中了(你只需要加一个@RequestBody注解)
System.out.println(user);
// 做响应,模拟查询数据库
user.setUsername("JT.L");
user.setAge(18);
// 做响应(查出来跟数据库不一样) -- MVC框架已经把事全做好了,只需要返回一个User对象即可
// 但是你返回的是一个对象,但是最终给前端的还是一个json数据,因为设置了返回的数据类型dataType:json
// 反对象不行得把对象转成json,但也不用你自己转,只需要加一个@ResponseBody注解
return user;
}
总结:整个的发送请求获得响应的过程都完成了,这就是以后用异步发送json数据开发方式。
第2章 SpringMVC实现文件上传
2.1 文件上传的回顾
2.1.1 文件上传的必要前提
1、form表单的enctype取值必须是:multipart/form-data(指定传输数据为二进制数据,例如图片、mp3、文件。http请求中的multipart/form-data,会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。 既可以上传键值对,也可以上传文件)
默认值:application/x-www-form-urlencoded(会将表单内的数据转换为键值对)
2、method属性取值必须是post:如果是get会把你请求的东西放在地址栏上,地址栏的大小是有限制的,装不了多少数据;大数据的提交必须选post。
3、提供一个文件选择域<input type=”file” />
2.1.2 文件上传的原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。
enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:key=value&key=value&key=value
当form表单的enctype 取值为Mutilpart/form-data时,请求正文内容就变成:
每一部分都是 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--
2.1.3 借助第三方组件实现文件上传
这些组件给我们提供了api,可以很方面的解析上传文件的请求体,不需要我们过于细节的去解析那个请求体(不需要我们知道请求体的格式也能正确解析)。这个组件是Apache提供的。
注:剩余内容见另一个博客