SpringMVC处理响应
配置视图解析器
SpringMVC默认情况下会在控制器执行完成后跳转到视图页面,视图解析器能找到相应的视图,之前的404异常就是由于没有配置视图解析器导致找不到视图。
在SpringMVC中提供了13个视图解析器,用于支持不同的视图技术。InternalResourceViewResolver是SpringMVC的默认视图解析器,用来解析JSP视图。
<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/" />
<!-- 视图后缀 -->
<property name="suffix" value=".jsp" />
</bean>
控制器方法的返回值
我们可以通过控制器方法的返回值设置跳转的视图,控制器方法支持以下返回值类型:
返回值为void
此时会跳转到名字是 前缀+方法路径名+后缀 的jsp页面
-
编写控制器方法
// 路径是helloMVC,方法执行完后会跳转到/helloMVC.jsp @RequestMapping("/helloMVC") public void helloMVC(){ System.out.println("hello SpringMVC!"); }
-
编写helloMVC.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>MVC</title> </head> <body> <h1>欢迎来到SpringMVC</h1> </body> </html>
返回值为String
此时会跳转到名字是 前缀+返回值+后缀 的jsp页面
编写控制器方法
// 返回值为String
@RequestMapping("/c2/hello1")
public String helloMVC1(){
System.out.println("hello SpringMVC!");
// 方法执行完后会跳转到/helloMVC.jsp
return "helloMVC";
}
返回值为ModelAndView
这是SpringMVC提供的对象,该对象可以向request域设置数据并指定跳转的页面。该对象中包含Model对象和View对象。
- Model:向request域中设置数据。
- View:指定跳转的页面。
-
编写控制器方法
// 返回值为ModelAndView @RequestMapping("/c2/hello2") public ModelAndView useMAV(){ System.out.println("返回值类型为ModelAndView"); // 1.创建ModelAndView对象 ModelAndView modelAndView = new ModelAndView(); // 2.获取Model对象,本质是一个Map Map<String, Object> model = modelAndView.getModel(); // 3.使用Model对象向request域设置数据 model.put("name","李四"); // 4.使用View对象设置跳转的路径为/baizhan.jsp modelAndView.setViewName("baizhan"); return modelAndView; }
-
编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java"%> <html> <head> <title>hello</title> </head> <body> <h1>你好!${requestScope.name}</h1> </body> </html>
-
修改web.xml命名空间,让jsp页面默认支持el表达式
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> </web-app>
request域设置数据
当控制器返回值为ModelAndView时我们可以向request域设置数据,我们还有以下方法可以向request域设置数据:
使用原生的HttpServletRequest
@RequestMapping("/c2/hello3")
public String setRequestModel(HttpServletRequest request){
request.setAttribute("username","张三");
return "baizhan";
}
使用Model、ModelMap
SpringMVC提供了Model接口和ModelMap类,控制器方法添加这两个类型的参数,使用该参数设置数据,该数据就会存到request域中。
@RequestMapping("/c2/hello4")
public String setRequestModel2(Model model, ModelMap modelMap){
// 使用Model将数据存入request域
// model.addAttribute("username","张三");
// 使用ModelMap将数据存入request域
modelMap.addAttribute("username","张三");
return "baizhan";
}
使用Map集合
Model接口底层就是一个Map集合,我们可以给控制器方法设置Map类型的参数,向Map中添加键值对,数据也会存到request域中。
@RequestMapping("/c2/hello5")
public String setRequestModel3(Map map){
map.put("username","张三");
return "baizhan";
}
session域设置数据
Session作用域表示在当前会话中有效。在SpringMVC中对于Session作用域传值,只能使用HttpSession对象来实现。
-
编写控制器方法
@RequestMapping("/c2/hello6") public String setSeesionModel(HttpSession session){ session.setAttribute("address","北京"); return "baizhan"; }
-
编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java"%> <html> <head> <title>hello</title> </head> <body> <h1>你好!${requestScope.name}</h1> <h1>地址是!${sessionScope.address}</h1> </body> </html>
context域设置数据
context作用域表示在整个应用范围都有效。在SpringMVC中对context作用域传值,只能使用ServletContext对象来实现。但是该对象不能直接注入到方法参数中,需要通过HttpSession对象获取。
-
编写控制器方法
@RequestMapping("/c2/hello7") public String setContextModel(HttpSession session){ ServletContext servletContext = session.getServletContext(); servletContext.setAttribute("age",10); return "baizhan"; }
-
编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java"%> <html> <head> <title>hello</title> </head> <body> <h1>你好!${requestScope.name}</h1> <h1>地址是!${sessionScope.address}</h1> <h1>年纪是!${applicationScope.age}</h1> </body> </html>
请求转发&重定向
之前的案例,我们发现request域中的值可以传到jsp页面中,也就是通过视图解析器跳转到视图的底层是请求转发。
如果我们跳转时不想使用视图解析器,可以使用原生HttpServletRequest进行请求转发或HttpServletResponse进行重定向:
@RequestMapping("/c2/hello8")
public void myForward1(HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setAttribute("name","张三");
// 请求转发
// request.getRequestDispatcher("/c2/hello9").forward(request,response);
// 原生重定向
response.sendRedirect("/c2/hello9");
}
@RequestMapping("/c2/hello9")
public void myForward2(HttpServletRequest request){
System.out.println("hello");
System.out.println(request.getAttribute("name"));
}
SpringMVC还提供了一种更简单的请求转发和重定向的写法:
@RequestMapping("/c2/hello10")
public String myForward3(HttpServletRequest request){
request.setAttribute("name","张三");
// 请求转发
return "forward:/c2/hello9";
// 重定向
return "redirect:/c2/hello9";
}
SpringMVC通过注解来实现控制器的功能,接下来我们详细学习SpringMVC的常用注解:
SpringMVC注解
@Controller
作用:标记控制器,将控制器交给Spring容器管理。
位置:类上方
@RequestMapping
作用:给控制器方法设置请求路径
位置:方法或类上方。用于类上,表示类中的所有控制器方法都是以该地址作为父路径。
属性:
- value/path:请求路径
- method:指定请求方式
- params:规定必须发送的请求参数
- headers:规定请求必须包含的请求头
@Controller
@RequestMapping("/c3")
public class MyController3 {
/*
访问路径为 /c3/annotation1
支持post和get请求
请求时必须带有age参数
请求时必须带有User-agent请求头
*/
@RequestMapping(path = "/annotation1",method = {RequestMethod.GET,RequestMethod.POST},params = {"age"},headers = {"User-agent"})
public String annotation1(String username){
System.out.println(username);
return "baizhan";
}
}
@RequestParam
作用:在控制器方法中获取请求参数
位置:方法参数前
属性:
- name:指定请求参数名称
- defaultValue: 为参数设置默认值
- required:设置是否是必须要传入的参数
/*
定义请求的参数名为username,默认值为lisi,不是必须的参数
*/
@RequestMapping("/annotation2")
public String annotation2(@RequestParam(name = "username",defaultValue = "lisi",required = false) String name){
System.out.println(name);
return "baizhan";
}
请求URL的写法:http://localhost:8080/c3/annotation2?username=bz
@RequestHeader、@CookieValue
@RequestHeader
作用:在控制器方法中获取请求头数据
位置:方法参数前
@CookieValue
作用:在控制器方法中获取Cookie数据
位置:方法参数前
/*
获取User-Agent请求头
获取JSESSIONID的Cookie值
*/
@RequestMapping("/annotation3")
public String annotation3(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String jSessionId){
System.out.println(userAgent);
System.out.println(jSessionId);
return "baizhan";
}
@SessionAttributes
作用:将Model模型中的数据存到session域中
位置:类上方
@Controller
@RequestMapping("/c4")
// 将模型中的name数据保存到session中
@SessionAttributes("name")
public class MyController4 {
@RequestMapping("/t1")
public String t1(Model model){
// model中保存name数据
model.addAttribute("name","张三");
return "baizhan";
}
@RequestMapping("/t2")
public String t2(HttpSession session){
// 从session中获取name数据
System.out.println(session.getAttribute("name"));
return "baizhan";
}
}
@ModelAttribute
作用1:设置指定方法在控制器其他方法前执行
位置:方法上方
@Controller
@RequestMapping("/c5")
public class MyController5 {
@ModelAttribute
public void before(){
System.out.println("前置方法");
}
@RequestMapping("/t1")
public String t1(){
System.out.println("t1");
return "baizhan";
}
}
作用2:从Model模型中获取数据给参数赋值
位置:方法参数前
@Controller
@RequestMapping("/c6")
public class MyController6 {
// 前置方法向Model中设置数据
@ModelAttribute
public void before(Model model){
model.addAttribute("name","张三");
}
// 该参数不是从请求中获取,而是从Model中获取
@RequestMapping("/t1")
public String t1(@ModelAttribute("name") String name){
System.out.println(name);
return "baizhan";
}
}
RESTful风格支持
RESTful风格介绍
RESTful风格是一种URL路径的设计风格。在RESTful风格的URL路径中,网络上的任意数据都可以看成一个资源,它可以是一段文本、一张图片,也可以是一个Java对象。而每个资源都会占据一个网络路径,无论对该资源进行增删改查,访问的路径是一致的。
传统URL:
查找id为1的学生:
http://localhost:8080/student/findById?id=30
删除id为1的学生:
http://localhost:8080/student/deleteById?id=30
RESTful风格URL:
查找id为30的学生:
http://localhost:8080/student/30
删除id为30的学生:
http://localhost:8080/student/30
那么如何区分对该资源是哪一种操作?通过请求方式不同,判断进行的是什么操作。
之前我们学过两种请求方式,GET请求和POST请求,而访问RESTful风格的URL一共有四种请求方式:
- GET请求:查询操作
- POST请求:新增操作
- DELETE请求:删除操作
- PUT请求:修改操作
RESTful风格URL:
查找id为30的学生:
http://localhost:8080/student/30 GET方式请求
删除id为30的学生:
http://localhost:8080/student/30 DELETE方式请求
RESTful风格的优点:
结构清晰、符合标准、易于理解、扩展方便。
Postman使用
默认情况下浏览器是无法发送DELETE请求和PUT请求的,我们可以使用Postman工具发送这些请求。
-
双击安装包安装Postman
-
点击new-collection创建请求集合
-
添加请求
-
保存请求到集合,以后可以随时发送该请求
@PathVariable
作用:在RESTful风格的URL中获取占位符的值
位置:方法参数前
属性:
- value:获取哪个占位符的值作为参数值,如果占位符和参数名相同,可以省略该属性。
@Controller
@RequestMapping("/student")
// 模拟学生的增删改查控制器
public class StudentController {
// 路径中的{id}表示占位符,最后会封装到方法的参数中使用
// 删除学生
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
public String deleteStudent(@PathVariable("id") int id){
System.out.println("删除id为"+id+"的学生");
return "baizhan";
}
// 如果占位符和参数名相同,可以省略@PathVariable的value属性
// 根据id查询学生
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public String findStudentById(@PathVariable int id){
System.out.println(id);
System.out.println("根据id查询学生");
return "baizhan";
}
// 新增学生
@RequestMapping(value = "/{id}",method = RequestMethod.POST)
public String addStudent(@PathVariable int id, Student student){
System.out.println(id);
System.out.println(student);
System.out.println("新增学生");
return "baizhan";
}
// 修改学生
@RequestMapping(value = "/{id}",method = RequestMethod.PUT)
public String updateStudent(@PathVariable int id, Student student){
System.out.println(id);
System.out.println(student);
System.out.println("修改学生");
return "baizhan";
}
}
访问方式:
- 新增学生:POST http://localhost:8080/student/1?name=张三&sex=男
- 修改学生:PUT http://localhost:8080/student/1?name=张三&sex=男
- 删除学生:DELETE http://localhost:8080/student/1
- 查询学生:GET http://localhost:8080/student/1
@PostMapping、@GetMapping、@PutMapping、@DeleteMapping
作用:简化设置请求方式的@RequestMapping写法
位置:方法上方。
@Controller
@RequestMapping("/student")
public class StudentController {
// 删除学生
@DeleteMapping("/{id}")
public String deleteStudent(@PathVariable("id") int id){
System.out.println("删除id为"+id+"的学生");
return "baizhan";
}
// 根据id查询学生
@GetMapping("/{id}")
public String findStudentById(@PathVariable int id){
System.out.println(id);
System.out.println("根据id查询学生");
return "baizhan";
}
// 新增学生
@PostMapping("/{id}")
public String addStudent(@PathVariable int id, Student student){
System.out.println(id);
System.out.println(student);
System.out.println("新增学生");
return "baizhan";
}
// 修改学生
@PutMapping("/{id}")
public String updateStudent(@PathVariable int id, Student student){
System.out.println(id);
System.out.println(student);
System.out.println("修改学生");
return "baizhan";
}
}
HiddentHttpMethodFilter
由于浏览器form表单只支持GET与POST请求,而DELETE、PUT请求并不支持,SpringMVC有一个过滤器,可以将浏览器的POST请求改为指定的请求方式,发送给的控制器方法。
用法如下:
-
在web.xml中配置过滤器
<!-- 请求方式过滤器 --> <filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>httpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
编写控制器方法
@Controller @RequestMapping("/c7") public class MyController7 { @DeleteMapping("/delete") public String testDelete(){ System.out.println("删除方法"); return "baizhan"; } @PutMapping("/put") public String testPut(){ System.out.println("修改方法"); return "baizhan"; } }
-
在jsp中编写表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>DELETE、PUT提交</title> </head> <body> <!-- 删除 --> <%-- 提交DELETE、PUT请求,表单必须提交方式为post --%> <%-- 表单中有一个隐藏域,name值为_method,value值为提交方式 --%> <form action="/c7/delete" method="post"> <input type="hidden" name="_method" value="DELETE"> <input type="submit" value="删除"> </form> <hr/> <!-- 修改 --> <form action="/c7/put" method="post"> <input type="hidden" name="_method" value="PUT"> <input type="submit" value="修改"> </form> </body> </html>
@ResponseBody
作用:方法返回的对象转换为JSON格式,并将JSON数据直接写入到输出流中,使用此注解后不会再经过视图解析器。使用该注解可以处理Ajax请求。
位置:方法上方或方法返回值前
-
编写jsp页面,发送ajax请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax请求</title> <script src="/js/jquery-2.1.1.min.js"></script> <script> $(function () { $("#btn").click(function () { var name = $("#name").val(); var sex = $("#sex").val(); $.get("/c8/addStudent",{"name":name,"sex":sex},function (data){ console.log(data); }); }); }); </script> </head> <body> 姓名:<input id="name"/><br/> 性别:<input id="sex"/><br/> <input type="button" value="提交" id="btn"/> </body> </html>
-
由于jsp页面中引入jQuery的js文件,而SpringMVC会拦截所有资源,造成jquery.js失效,需要在SpringMVC核心配置文件中放行静态资源。
<!-- 放行静态资源 --> <mvc:default-servlet-handler />
-
编写结果实体类,该实体类会封装一个请求的结果
// 请求的结果对象 public class Result { private boolean flag; // 请求是否成功 private String message; // 请求提示信息 // 省略getter/setter/构造方法 }
-
编写控制器
@PostMapping("/addStudent") @ResponseBody public Result addStudent(String name, String sex) { // 输出接受到的参数,模拟添加学生 System.out.println(name+":"+sex); // 返回添加结果 Result result = new Result(true, "添加学生成功!"); return result; }
-
SpringMVC会将Result对象转为JSON格式写入输出流,而SpringMVC默认使用的JSON转换器是jackson,需要在pom中添加jackson依赖。
<!-- jackson --> <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-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency>
@RestController
如果一个控制器类下的所有控制器方法都返回JSON格式数据且不进行跳转,可以使用@RestController代替@Controller,此时每个方法上的@ResponseBody都可以省略。
@RestController
@RequestMapping("/c8")
public class MyController8 {
@PostMapping("/addStudent")
public Result addStudent(String name, String sex) {
// 输出接受到的参数,模拟添加学生
System.out.println(name+":"+sex);
// 返回结果
Result result = new Result(true, "添加学生成功!");
return result;
}
}
静态资源映射
当在DispatcherServlet的<url-pattern>
中配置拦截 “/” 时,除了jsp文件不会拦截以外,其他所有的请求都会经过前端控制器进行匹配。此时静态资源例如css、js、jpg等就会被前端控制器拦截,导致不能访问,出现404问题。想要正常映射静态资源共有三种方案:
配置静态资源筛查器
在SpringMVC的配置文件中配置<mvc:default-servlet-handler />后,会在Spring容器中创建一个资源检查器,它对进入DispatcherServlet的URL进行筛查,如果不是静态资源,才由DispatcherServlet处理。
修改SpringMVC核心配置文件:
<mvc:default-servlet-handler/>
配置静态资源映射器
SpringMVC模块提供了静态资源映射器组件,通过<mvc:resources>
标签配置静态资源映射器,配置后的路径不会由DispatcherServlet处理。
修改SpringMVC核心配置文件:
<!--配置静态资源映射器-->
<!-- mapping:配置请求的URL location:资源路径-->
<mvc:resources mapping="/img/" location="/img/"/>
<mvc:resources mapping="/js/" location="/js/"/>
配置默认Servlet处理静态资源
在web.xml可以配置默认Servlet处理静态资源,该Servlet由tomcat提供,它会直接访问静态资源不进行其他操作。这样就避免了使用DispatcherServlet对静态资源的拦截:
修改web.xml:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
@RequestBody
作用:将请求中JSON格式的字符串参数转为JAVA对象
位置:写在方法参数前
-
AJAX请求发送JSON格式的参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax请求</title> <script src="/js/jquery-2.1.1.min.js"></script> <script> $(function (){ $("#btn").click(function (){ var name=$("#name").val(); var sex=$("#sex").val(); var param = JSON.stringify({"name":name,"sex":sex}); $.ajax({ url:"/c8/addStudent2", contentType:"application/json", type:"post", data:param, success:function (data){ console.log(data); } }) }) }) </script> </head> <br> 姓名:<input id="name"><br /> 性别:<input id="sex"><br /> <input type="button" value="提交" id="btn"> </body> </html>
-
编写控制器
@PostMapping("/addStudent2") @ResponseBody public Result addStudent2(@RequestBody Student student) { System.out.println(student); // 返回添加结果 Result result = new Result(true, "添加学生成功!"); return result; }