SpringMVC(二) 页面转发、重定向、异步、Restful、异常处理、拦截器
页面跳转之转发
方式一:简单方式
- 页面
<a href="${pageContext.request.contextPath}/respServlet/forward1">普通转发</a>
- 后台
//页面转发方式1
@RequestMapping("/respServlet/forward1")
public String forward1() {
return "success";
}
方式二:使用forward转发
在返回中使用forward:url表示使用forward转发
- 页面
<a href="${pageContext.request.contextPath}/respServlet/forward2">forward转发</a>
- 后台
@RequestMapping("/forward2")
public String forward2() {
return "forward:/WEB-INF/fail.jsp";
}
方式三:使用Servlet原生API
通过SpringMVC前端控制器传递回来的参数可以接收到HttpServletRequest HttpServletResponse对象,使用ServletAPI可以进行转发
- 页面
<a href="${pageContext.request.contextPath}/respServlet/forward3">Servlet原生API</a>
- 后台
@RequestMapping("/forward3")
public void forward3(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/fail.jsp")
.forward(request, response);
}
转发携带数据
进行页面转发时,通常我们都会携带数据,SpringMVC中我们
- 可以使用HttpServletRequest request
@RequestMapping("/forward4")
public String forward4(HttpServletRequest request) {
//使用Request传值
request.setAttribute("pageValue", "zs");
return "success";
}
- 还可以使用Model 对象
@RequestMapping("/forward5")
public String forward5(Model model) {
//传递数据
model.addAttribute("pageValue", "ss");
return "success";
}
- 还可以使用ModelAndView对象
@RequestMapping("/forward6")
public ModelAndView forward6() {
//构造ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//设置数据
modelAndView.addObject("pageValue", "fromModelAndView");
//设置逻辑视图
modelAndView.setViewName("success");
return modelAndView;
}
页面跳转之重定向
使用redirect重定向
在Controller的返回中使用 redirect:url代表页面重定向
- 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect1">redirect重定向</a>
- 后台
@RequestMapping("/redirect1")
public String redirect1() {
return "redirect:/success.jsp";
}
使用Servlet原生API
- 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect2">使用Servlet原生API</a>
- 后台
@RequestMapping("/redirect2")
public void redirect2(HttpServletRequest request,
HttpServletResponse response) throws IOException {
response.sendRedirect(request.getContextPath() + "/success.jsp");
}
使用重定向的方式进入外界无法访问的WEB-INF目录下
我们知道在javaWeb项目中,WEB-INF目录下的资源是无法通过外界访问的。但是可以通过服务器内部的请求转发访问到,现在我们想使用页面重定向技术进行访问WEB-INF目录下的资源。我们可以经过一次转发来达到通过重定向技术来访问WEB-INF目录下的资源
- 页面
<a href="${pageContext.request.contextPath}/respServlet/redirect3">使用重定向-转发来访问WEB-INF下的资源</a>
-
后台
@RequestMapping("/redirect3") public String redirect3() { return "redirect:/respServlet/redirforwardtoSuccess"; } //转发方法 @RequestMapping("/redirforwardtoSuccess") public String redirectForward() { //转发到指定资源中 return "forward:/WEB-INF/fail.jsp"; }
释放静态资源
当有静态资源需要加载时,比如js css 等,如果在url-pattern中配置的是/,代表除了.jsp请求不拦截,其他的所有请求都会拦截,包括一些静态文件,而拦截之后,SpringMVC又找不到对应的处理器来处理,因此我们需要释放静态资源,来让静态资源能够被正常加载
方式一
<!--
释放资源:
mapping:代表匹配的路径规则
location:代表匹配的路径规则的静态资源的位置
-->
<mvc:resources mapping="/js/*" location="/js/"/>
方式二
指定处理器映射器寻找不到对应的处理方法时,暂时不要报错,而是直接丢给外部的默认Servlet,也就是tomcat处理
<!-- 指定错误交给外部的处理器处理-->
<mvc:default-servlet-handler/>
Ajax+json实现异步交互
在SpringMVC中,前端发送Ajax异步请求,后端返回json响应。这个功能主要是通过两个注解@RequestBody和@ResponseBody实现的
SpringMVC默认使用MappingJackson2HttpMessageConverter对json数据进行转换,需要加入jackson的包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
@RequestBody接收ajax请求
@RequestBody用于接收前端传递的请求体重的json数据,并可以自动转换封装进指定的对象中
- 页面
- 其中contentType代表请求参数类型
- dataType代表响应数据类型
<button id="btn_ajax">提交ajax请求</button>
<script src="/js/jquery-3.3.1.js"></script>
<script>
$(function () {
$("#btn_ajax").click(function () {
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/respServlet/ajaxReq",
contentType: "application/json",
dataType: "json",
data: '[{"name":"张三","age":19},{"name":"李四","age":19}]',
success: function (data) {
console.log(data);
alert(data);
}
});
});
});
</script>
- 封装实体类
public class User {
private String name;
private Integer age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
- 后台
@ResponseBody
@RequestMapping("/ajaxReq")
public List<User> ajaxReq(@RequestBody List<User> users) {
System.out.println("");
users.stream().forEach(user -> System.out.println(user));
return users;
}
@ResponseBody返回Json数据
@responseBody用于将controller方法返回的对象通过转换器转换为指定的格式(通常为json),
@ResponseBody
@RequestMapping("/ajaxReqStr")
public String ajaxReqStr(@RequestBody User user) {
System.out.println(user);
return "ok";
}
Spring还提供了一个@RestController 表示@Controller+@ResponseBody
Restful风格
定义
REST是一种软件架构风格,其强调HTTP应当以资源为中心
REST规范了HTTP请求动作,使用四个词语分别表示对资源的CRUD操作
也就是说Restful风格是通过方法的method类型来区分各种不同的业务
原来 | Restful | |
---|---|---|
保存 | /saveUser | post /usr |
修改 | /updateUser?id=1 | Put /user/1 |
删除 | /deleteUser?id=1 | delete /user/1 |
查询所有 | /findAllUser | get /user |
查询一个 | /findUserById?id=1 | get /user/1 |
保存
- 页面
$("#save").click(function () {
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/user",
contentType: "application/json",
dataType: "text",
data: '{"name":"user1","age":11}',
success: function (data) {
alert(data);
}
});
})
- 后台
注解@PostMapping 相当于@RequestMapping(value="/user" method="POST")
@PostMapping("/user") //@PostMapping 相当于 @RequestMapping(value = "/user",method = RequestMethod.POST)
@ResponseBody
public String saveUser(@RequestBody User user) {
System.out.println("收到的参数" + user);
return "ok";
}
查询
get请求没有请求体,所以参数需要在请求地址中传递
- 页面
$("#find").click(function () {
$.ajax({
type: "GET",
url: "${pageContext.request.contextPath}/user/name/user1/age/11",
contentType: "application/json",
dataType: "text",
success: function (data) {
alert(data);
}
});
});
- 后台
@GetMapping("/user/name/{name}/age/{age}")
@ResponseBody
public String findUser(@PathVariable String name,
@PathVariable String age) {
System.out.println("接收到的参数" + name + age);
return "ok";
}
异常处理机制
Spring框架,我们通常将异常抛到框架中,然后指定Spring的异常处理器来统一处理异常
方式一:自定义异常处理器
可以实现HandlerExceptionResolver接口,实现接口方法来自定义异常处理器
public class MyExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
return new ModelAndView("error", "err", e.getMessage());
}
}
- 加入扫描注解
<context:component-scan base-package="com.itheima.exceptionhandler"/>
方式二:@ControllerAdvice
使用注解@ControllerAdvice注解 @ExceptionHandler(Exception.class)
@ControllerAdvice
public class MyExceptionHandler2 implements HandlerExceptionResolver {
//声明处理哪些异常
@ExceptionHandler(Exception.class)
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
return new ModelAndView("WEB-INF/error", "err", e.getMessage());
}
}
拦截器
什么是拦截器
拦截器是Spring提供的一种技术,类似于Servlet的Filter
拦截器的特点
- 会在请求进入controller之前
- 离开controller之后
- 页面渲染完毕之后
自定义拦截器
自定义一个类实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入控制器之前");
//代表是否放心 true 代表放行 false代表不放心
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("离开控制器之后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面渲染完成之后");
}
}
- Springmvc中配置拦截规则
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 要拦截的路径规则 /**代表拦截所有-->
<mvc:mapping path="/**"/>
<!-- 不拦截的路径规则-->
<mvc:exclude-mapping path="/login"/>
<!-- 指定拦截器-->
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
自定义拦截器链
开发中拦截器可以单独使用,也可以同时使用多个拦截器形成一条拦截器链
开发步骤和单个拦截器是一样的,只不过注册多个拦截器的时候,注册的顺序代表拦截器的执行顺序
- 定义第二个拦截器
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:mapping path="/register"/>
<bean class="com.itheima.interceptor.MyInterceptor2"/>
</mvc:interceptor>
拦截器和过滤器的区别
- 拦截器属于SpringMVC框架,只有使用了SpringMVC框架才能使用拦截器,Servlet属于JavawebAPI 只要是JavaWeb工程都可以使用
- 过滤器在url-pattern中配置了/之后,可以对所有资源拦截,拦截器配置了/ 之后 只会拦截访问的控制器方法,不会拦截静态资源
案例 使用拦截器完成用户访问的拦截
req:
- 用户访问一个页面index.jsp
- 如果用户已经登录,可直接进入index.jsp
- 如果用户没有登录,跳转到登录页面
- 页面
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="text" name="password"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录完成 ${username}
</body>
</html>
- 后台Controller
@Controller
public class UserController {
@RequestMapping("/login")
public String login(String username, String password, HttpSession session) {
if (username != null && !username.equals("")) {
if (username.equals("admin")) {
//登录成功 存入session
session.setAttribute("loginName", username);
return "forward:/index";
}
}
return "redirect:/login.jsp";
}
@RequestMapping("/index")
public String toIndex() {
return "WEB-INF/index";
}
}
- 拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断是否已经存储
String loginName = (String) request.getSession().getAttribute("loginName");
if (loginName != null && !"".equals(loginName)) {
//不为空 放行
return true;
} else {
//为空 重定向到登录页面
response.sendRedirect(request.getContextPath() + "/login.jsp");
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 拦截器配置
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.itheima.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>