(一)请求转发与重定向
- 在SpringMVC的控制单元中,方法定义返回类型可以有多种。理论上是返回任意类型都对。包括void。
- 忽略AJAX请求处理方式,请求转发和响应重定向对应的控制单元返回值类型包括:
-
1. String - 返回的字符串,默认就是结果视图的地址。
-
默认是请求转发。实际上,类似指定请求转发方案 forward:/path
-
响应重定向: redirect:/path
-
2. ModelAndView - 使用Model传递请求作用域数据,使用view传递结果视图地址。
-
默认的结果视图地址就是请求转发,可以使用类似的功能语法 forward:/path 提供viewName,
-
可以使用redirect:/path 提供重定向viewName。
-
如果使用ModelAndView实现重定向,那么其中的model请求作用域数据,会自动转换成同名请求参数传递
-
3. void - 使用ServletAPI实现请求转发和重定向。
- 如果结果路径是其他的控制器RequestMapping地址,编程方式和普通视图返回处理一致。
- 注意:如果返回的路径使用forward:或redirect:开头,路径和前缀一定要紧挨在一起。
- 注意:所有的返回结果地址,建议都使用 ‘/’ 开头,都是相对应服务器的根开始寻址。准确。
@RequestMapping("/servletRedirect")
public void servletRedirect(HttpServletResponse response) throws IOException, ServletException{
System.out.println("servletRedirect运行");
response.sendRedirect("/index.jsp");
}
@RequestMapping("/servletForward")
public void servletForward(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
System.out.println("servletForward运行");
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
@RequestMapping("/redirectMV")
public ModelAndView redirectMV(){
System.out.println("redirectMV运行");
ModelAndView mv = new ModelAndView();
mv.addObject("t", "测试重定向");
mv.setViewName("redirect:/index.jsp");
return mv;
}
@RequestMapping("/forwardMV")
public ModelAndView forwardMV(){
System.out.println("forwardMV运行");
ModelAndView mv = new ModelAndView();
mv.addObject("t", "测试数据传递");
mv.setViewName("forward:/index.jsp");
// mv.setViewName("/index.jsp");
return mv;
}
//返回结果为字符串
@RequestMapping("/testRedirect")
public String testRedirect(){
System.out.println("testRedirect方法运行");
return "redirect:/index.jsp";
}
@RequestMapping("/testForward")
public String testForward(){
System.out.println("testForward方法运行");
return "forward:/index.jsp";
// return "/index.jsp";
}
(二)视图解析器
配置视图解析器,这是一个bean对象。这个对象,由springmvc框架自动使用。是基于byType找对象的。
可以不配置唯一标记。 id或name
这就是默认的视图解析器。在sping-mvc.xml中配置
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 可以配置属性 -->
<!-- 默认情况下,返回的字符串viewName,会自动拼接前后缀,组成完整的视图地址。 -->
<!-- 统一的前缀 -->
<!--<property name="prefix" value="/WEB-INF/jsp/"></property>-->
<!-- 统一的后缀 -->
<!--<property name="suffix" value=".jsp"></property>-->
</bean>
- Java:
@RequestMapping("/test1")
public String direct(){
System.out.println("隐私中的direct运行");
return "index";
}
(三)变量作用域
1.request
-
1. 使用ModelAndView : 使用Model传递数据
-
2. 使用控制单元(方法)参数,Map类型的参数。 直接在方法参数表中定义Map类型的参数,put的键值对,自动传递到请求作用域
-
3. 使用控制单元参数, Model类型的参数。 在方法参数表中定义Model类型的参数,调用add系列方法,自动传递到请求作用域。
-
4. 使用ServletAPI, request.setAttribute。 Servlet原生API。
@RequestMapping("/reqScopeModel")
public String reqScoopeModel(Model model){
System.out.println(model.getClass().getName());
// 相当于request.setAttribute
model.addAttribute("msg", "使用Model传递的数据");
Map<String, Object> attrs = new HashMap<>();
attrs.put("testMap", "测试使用addAllAttributes传递数据");
// 迭代map,使用map的key作为attribute名字,map的value作为attribute值,调用addAttribute方法。
model.addAllAttributes(attrs);
return "/bjsxt/getReqScope";
}
@RequestMapping("/reqScopeMap")
public String reqScopeMap(Map<String, Object> map){
System.out.println(map.getClass().getName());
map.put("msg", "使用Map集合传递的数据");
return "/index.jsp";
}
@RequestMapping("/reqScopeMV")
public ModelAndView reqScopeMV(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "使用ModelAndView传递的数据");
mv.setViewName("/index.jsp");
return mv;
}
- 取值
-
1. 使用ServletAPI, request.getAttribute
-
2. 使用注解,让SpringMVC帮助从请求作用域中获取attribute。
// @RequestAttribute获取request中的值
@RequestMapping("/testAtt")
public String test(@RequestAttribute("msg") String msg,@RequestAttribute("hash") String h){
System.out.println("请求作用域: msg = " + msg);
System.out.println("请求作用域: testMap = " + h);
return "/index.jsp";
}
- 2.会话变量作用域: session
- 设置:
-
1. 只能通过ServletAPI实现。
- 取值:
-
1. 使用ServletAPI获取, request.getSession().getAttribute() 或者 session.getAttribute()
-
2. 使用方法参数+注解实现。
@RequestMapping("/getSessionScope")
public String getSessionScope(@SessionAttribute("session1") String session1,
@SessionAttribute String session2){
System.out.println("session1 = " + session1);
System.out.println("session2 = " + session2);
return "/index.jsp";
}
@RequestMapping("/sessionScope")
public String sessionScope(HttpServletRequest request, HttpSession session){
request.getSession().setAttribute("session1", "基于request.getSession.setAttribute传递");
session.setAttribute("session2", "基于session.setAttribute传递");
return "/index.jsp";
}
- 应用上下文变量作用域: application。避免全局直接使用,存在的安全隐患。 不推荐使用。
- 设置:
-
1. 使用ServletAPI实现。 request.getServletContext().setAttribute()
- 取值:
-
1. 使用ServletAPI实现。 request.getServletContext().getAttribute()
@RequestMapping("/getAppScope")
public String getAppScope(HttpServletRequest request){
System.out.println("application app = " + request.getServletContext().getAttribute("app"));
return "/index.jsp";
}
@RequestMapping("/applicationScope")
public String applicationScope(HttpServletRequest request){
request.getServletContext().setAttribute("app", "通过application传递的数据");
return "/index.jsp";
}
(四)配置静态资源放行
<!-- 配置静态资源放行
配置例外,当请求地址是什么的时候,前端控制器DispatcherServlet不处理。直接放行请求到静态资源。
mapping - 请求地址的映射规则, 使用通配符配置。 如 /pic/*
location - 静态资源在哪一个目录中,寻址方案是,把mapping的*统配部分作为目录中的资源查找。
如: 请求地址是 /pic/1.webp, mapping配置的*对应 1.webp。在location文件夹/pic/中找 1.webp
静态资源放行,相当于请求转发。实际上,这个标签,相当于配置了一个controller。
mapping就是controller中方法的@RequestMapping地址。 location相当于方法的返回结果拼接方案。
ant映射方式: 使用同配置配置映射地址。可用位置: 所有springmvc的映射地址配置。
如: 静态资源放行、 @RequestMapping
单级别统配: *。 如: /pic/* , 代表目录/pic/中的任意资源。不包括子目录。
/pic/* 对应 /pic/1.webp /pic/abc /pic/123 /pic/000
多级别统配: /**。 如: /pic/**, 代表目录/pic/中的任意资源,包括子目录。
/pic/** 对应 /pic/1.webp /pic/1/1.webp /pic/abc /pic/a/b/c
-->
<mvc:resources mapping="/pic/**" location="/pic/"></mvc:resources>
<mvc:resources mapping="/pic2/**" location="/WEB-INF/pic/"></mvc:resources>
(五)restful风格
- DELETE 请求,没有请求体
- 传参方式必须:
-
- ?传参
-
- restFul风格参数。 /path/参数
-
springMVC的控制器,处理restful风格参数,必须给方法参数加注解。
-
PathVariable(value="requestMapping中的{}内的变量名,如果和参数名一样可以省略属性")
@DeleteMapping("/user/{suibian}")
@ResponseBody
public String removeUser(@PathVariable("suibian") Integer id){
System.out.println("删除: " + id);
return "删除成功";
}
-
PUT 请求, 没有请求体
-
传参方式
-
- ?传参
-
- restFul风格参数。 /path/参数1/参数2/参数3/…/参数n
-
restFul风格参数 不能处理 ‘/’。
@PutMapping("/user/{id}/{name}")
@ResponseBody
public String modifyUser(@PathVariable Integer id, @PathVariable String name){
System.out.println("修改:id = " + id + " , name = " + name);
return "修改";
}
(六)@ResponseBody
- @ResponseBody - 控制器方法的返回值,通过响应输出流,输出到客户端浏览器。
- 根据控制单元(方法)返回值类型,决定输出到客户端时,ContentType响应头类型的。
- 当方法增加@ResponseBody注解的时候,@RequestMapping注解的属性produces可以用于设置响应头contentType
- @ResponseBody 注解,在向客户端输出数据的时候,是需要转换方法返回值 到 客户端(浏览器)可识别的内容的。
- 这个转换是基于SpringMVC框架中的HttpMessageConverter Http协议消息转换器实现的。
- 需要项目中导入依赖 jackson 相关依赖。 才能让HttpMessageConverter 生效。
- SpringMVC 认为,正常情况下, 响应输出流向客户端输出基本数据类型的可能性很低。没有提供对应的HttpMessageConverter实现
- 使用的是通用实现。可以删除produces属性。做尝试。不同的springmvc版本jar包,提供不能的服务能力。
- 所谓通用实现,根据导入的jackson相关依赖不同,使用不同的实现。
- jackson-databind - 通用实现是json转换。
-
- 基本数据类型 - 默认设置响应头 content-type = application/json。 把方法返回值直接输出到客户端浏览器。
-
- String类型 - 默认设置响应头 content-type=text/html;charset=ISO-8859-1。
-
认为返回的字符串,是一个可显示的HTML视图。把字符串返回值直接输出到客户端。
-
可以通过Mapping的属性produces设置响应头,规避乱码问题。
-
- 其他引用类型 - 设置响应头 content-type=application/json。
-
使用HttpMessageConverter中的json转换器,把Java对象,转换成JSON格式字符串。
-
转换过程,基于property。property的名字是json中的属性名,property的value是json中的属性值。
-
注意:返回的引用类型中,getter和setter必须成对出现,除getClass外。
-
异常情况: 当处理此数据的反序列化时,会抛出异常。 把JSON格式字符串,转换成Java类型对象时。
-
且jackson-databind版本过低的时候,可能在java转换json格式字符串的时候,就会抛出异常。
-
- 集合类型 - 设置响应头 content-type=application/json。
-
Map - 效果等用引用类型对象。
-
List和Set - 是增加了JSON数组的引用类型对象。也就是[{},{}]
- 总结:
-
- 返回String, content-type = text/html;charset=ISO-8859-1
-
- 返回其他, content-type=application/json
@GetMapping("/testSet")
// @ResponseBody
public Set<User> testSet(){
//public @ResponseBody Set<User> testSet(){
System.out.println("返回Set集合");
Set<User> set = new HashSet<>();
set.add(new User(1, "吕布"));
set.add(new User(2, "貂蝉"));
return set;
}
@GetMapping("/testList")
// @ResponseBody
public List<User> testList(){
System.out.println("返回List集合");
List<User> list = new ArrayList<>();
list.add(new User(1, "关羽"));
list.add(new User(2, "张飞"));
list.add(new User(3, "刘备"));
return list;
}
@GetMapping("/testMap")
// @ResponseBody
public Map<String, Object> testMap(){
System.out.println("返回Map集合");
Map<String, Object> map = new HashMap<>();
map.put("name", "李四");
map.put("gender", "男");
return map;
}
@GetMapping("/testRef")
// @ResponseBody
public User testReturnReference(){
System.out.println("返回引用类型对象");
User user = new User();
user.setId(10);
user.setName("张三");
return user;
}
(七)@requestBody
@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List<Map>等类型。
既然是转换为请求体数据,所以不能是GET类型请求(GET没有请求体),多用在POST类型的请求中。
@RequestBody注解在单体架构项目使用的不是特别多。主要用在分布式项目中多个项目之间传递数据或一些开发平台中(例如微信开发平台接口返回XML数据)
如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML
@PostMapping("/listUser")
public List<User> listUser(@RequestBody List<User> userList){
System.out.println(userList);
return userList;
}
@PostMapping("/listMap")
public List<Map<String, Object>> listMap(@RequestBody List<Map<String, Object>> listMap){
System.out.println(listMap);
return listMap;
}
@PostMapping("/map")
public Map<String, Object> map(@RequestBody Map<String, Object> map){
System.out.println(map);
return map;
}
/**
* 请求参数中,包含一个用户对象。
*
* 使用要求:
* 1. 请求方式必须POST
* 2. 请求content-type必须是application/json
* 3. 请求参数使用请求体传递,必须是JSON格式字符串。
*
* RequestBody参数,使用JSON描述请求参数,理论上可以传递任何参数。
* 如:自定义对象,List,数组,Map等。
* 底层也是使用jackson-databind实现的。
*
* @param user
* @return
*/
@PostMapping("/testRequestBodyUser")
public User testRequestBody1(@RequestBody User user){
System.out.println(user);
return user;
}
其中前端请求代码:
<script src="/js/jquery.js"></script>
<script>
/**
* 当请求content-type=application/json时,
* 使用ajax发起请求,一定要设置dataType(响应数据类型)。
* 只有content-type和dataType同时设置,请求体参数,才会封装成json对象。
* 否则都是 参数名=参数值&参数名=参数值 的字符串|表单域。
*
* 请求参数JSON对象,被AJAX处理后,转换成了名值对表单参数。
* 使用JSON.stringify(JSON对象),手工转换对象成为JSON格式字符串。可以实现请求体传递JSON格式字符串。
*
* JSON.stringify(JSON对象) - 是把参数对象,转换成JSON格式字符串的方法。
* JSON对象 {"id":1, "name":"admin"}
* JSON格式字符串是 "{\"id\":1, \"name\":\"admin\"}" '{"id":1, "name":"admin"}'
*
*/
$(function () {
$("#btn").on('click', function () {
var data = {"id":$("#id").val(), "name":$("#name").val()};
$.ajax({
'url' : '/testRequestBodyUser',
'type' : 'post',
'contentType' : "application/json",
// 'data' : JSON.stringify(data),
'data' : data,
'success' : function (data) {
alert(data);
},
'dataType':'json' // 响应的
});
})
$("#mapBtn").on('click', function () {
var data = {"name":"张三", "gender" : "男"};
$.ajax({
'url': '/map',
'type': 'post',
'data':JSON.stringify(data),
'contentType': 'application/json',
'dataType': 'json',
'success': function (data) {
alert(data);
}
});
})
$("#listMapBtn").on('click', function () {
var data = [];
data.push({"name":"李四", "gender":"男"});
data.push({"name":"王五", "gender":"男"});
$.ajax({
'url': '/listMap',
'type': 'post',
'data':JSON.stringify(data),
'contentType': 'application/json',
'dataType': 'json',
'success': function (data) {
alert(data);
}
});
})
$("#listUserBtn").on('click', function () {
var data = [];
data.push({"id":1, "name":"赵四"});
data.push({"id":2, "name":"刘能"});
$.ajax({
'url': '/listUser',
'type': 'post',
'data':JSON.stringify(data),
'contentType': 'application/json',
'dataType': 'json',
'success': function (data) {
alert(data);
}
});
})
});
</script>