Spring MVC拦截器和全局异常处理(RESTful+状态码) 您所在的位置:网站首页 状态码c000011怎么处理 Spring MVC拦截器和全局异常处理(RESTful+状态码)

Spring MVC拦截器和全局异常处理(RESTful+状态码)

#Spring MVC拦截器和全局异常处理(RESTful+状态码)| 来源: 网络整理| 查看: 265

文章目录 概述第一步:实现`HandlerInterceptor`接口第二步:配置拦截器登录拦截数据准备视图 jsp 准备业务层控制层拦截器类配置文件测试 RESTful支持Spring MVC 统一异常处理全局处理 也有两种方式springmvc 提供的自定义的注解用 `@ControllerAdvice+@ ExceptionHandler` 注解(推荐)

概述 对于任何优秀的MVC框架,都会提供一些通用的操作,如请求数据的封装、类型转换、数据校验、解析上传的文件、防止表单的多次提交等。早期的MVC框架将这些操作都写死在核心控制器中,而这些常用的操作又不是所有的请求都需要实现的,这就导致了框架的灵活性不足,可扩展性降低。Spring MVC中的拦截器 (Interceptor)类似于 Servlet 中的过滤器(Filter),主要用于拦截用户的请求并作相应的处理。例如:权限验证、记录请求信息的日志、判断用户是否登录等。Spring MVC拦截器是可插拔式的设计,需要某一功能拦截器,只需在配置文件中应用该拦截器即可;如果不需要这个功能拦截器,只需在配置文件中取消应用该拦截器。

要使用拦截器需要对拦截器类进行定义和配置。有两种实现方式

实现 HandlerInterceptor接口实现 WebRequestInterceptor接口【两个使用时一样的】

以第一种方式为练习。

第一步:实现HandlerInterceptor接口

该接口的位置:org.springframework.web.servlet包中,定义了三个 default 方法,它们都有方法体。自定义的拦截器类实现该接口的时候需要实现这三个方法。【注意实现方法的时候方法修饰符是 public, 粘贴过来记得改】

boolean preHandle() 该方法在执行控制器方法之前执行。返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。 void postHandle() 该方法在执行控制器方法调用之后,且在返回ModelAndView之前执行。由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图【处理完视图再去跳转】,可通过此方法对请求域中的模型和视图做进一步的修改。 afterCompletion 该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。 image-20211202145146778 第二步:配置拦截器

我的 ssm 项目的主体配置都是 springmvc.xml 中进行的,拦截器配置是有先后顺序的。

拦截器链标签拦截器标签配置拦截路径 /**:是根下的全部文件 包括子目录/*:不包括子目录 :不拦截的路径,如果分不清楚可以在拦截全部的下面选出不拦截的 登录拦截 数据准备

用户实体:UserInfo

public class UserInfo implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String userName; private String userPass; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPass() { return userPass; } public void setUserPass(String userPass) { this.userPass = userPass; } }

用户映射文件:UserInfoMapper接口和UserInfoMapper.xml映射文件

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> select * from userinfo where username = #{userName} public interface UserInfoMapper { public UserInfo login(String userName); } 视图 jsp 准备 login文件夹下面的 login.jsp注意这些都是配置视图解析器之后在 WEB-INF/views下面的操作el 表达式都是取的错误信息 登录 用户登录${loginErr0} 用户名: ${loginErr} 密码: ${loginErr1} 业务层

传输用户名到这 调用接口的方法去查询数据【条件是用户名】

不顺便判断密码是为了在 controller 里面返回不同的错误信息回显

@Service public class UserInfoService { @Autowired UserInfoMapper mapper = null; /** * 根据用户名判断用户是否存在 * @param userName * @return */ public UserInfo login(String userName) { UserInfo userInfo = mapper.login(userName); if (userInfo!=null) { return userInfo; } return null; } } 控制层

先整个跳转到 login.jsp的controller, WEB-INF 路径不可直接在外部访问

尤其要注意最后跳转的路径即页面和映射的访问路径之间的区别

把查询到的用户信息 user 放到 session 中,通过 session 保存

old 是页面输入传递过来的数据组合的实体 user 是数据库中存在的实体数据

@Controller @RequestMapping("/userinfo") public class UserInfoController { @Autowired UserInfoService service = null; @RequestMapping("/login") public String login() { return "/login/login"; } @RequestMapping("/doLogin") public String doLogin(Model model ,String userName, String userPass, HttpSession session) { UserInfo user = service.login(userName); UserInfo old = new UserInfo(); old.setUserName(userName); old.setUserPass(userPass); if (user != null) { if (user.getUserPass().equals(userPass)) { session.setAttribute("user", user); return "index"; } model.addAttribute("loginErr1", "用户密码错误"); model.addAttribute("old", old); return "/login/login"; } model.addAttribute("loginErr", "用户账号错误"); model.addAttribute("old", old); return "/login/login"; } } 拦截器类

拦截器包

拦截器类:LoginHandlerInterceptor

顺便可以打印测试顺序

session 取出存放的用户实体,为空则没有登陆,跳到登录页面,不为空则已经登录,可以正常使用,拦截器向下走

public class LoginHandlerInterceptor implements HandlerInterceptor{ /** * 在controller方法之前调用 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // System.out.println("pre"); Object obj = request.getSession().getAttribute("user"); if (obj == null) { request.setAttribute("loginErr0", "您还没有登录"); request.getRequestDispatcher("/WEB-INF/views/login/login.jsp").forward(request, response); return false; } return true; } /** * 在跳转页面之前调用【业务执行完了】 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("return" + "之前"); } /** * 在请求结束后 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("controller执行结束之后"); } } 配置文件 前面的配置文件 测试 RESTful支持

概念:RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用 XML 格式定义或 JSON 格式定义。最常用的数据格式是JSON。由于JSON能直接被JavaScript读取,所以,使用JSON格式的REST风格的API具有简单、易读、易用的特点。

资源:REST 是面向资源的,每个资源都有一个唯一的资源定位符(URI)。每个URI代表一种资源(resource),所以URI中不能有动词,只能有名词,而且所用的名词往往与数据库的表名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以URI中的名词也应该使用复数。

请求方式:

请求方式含义GET(SELECT)从服务器取出资源(一项或多项)POST(CREATE)在服务器新建一个资源PUT(UPDATE)在服务器更新资源(更新完整资源)PATCH(UPDATE)在服务器更新资源, PATCH更新个别属性DELETE(DELETE)从服务器删除资源

【1】查询

查询 传统 REST REST 后台接收 查询所有 http://localhost:8080/employee/list http://localhost:8080/employees @RequestMapping(value = “/employees”, method = RequestMethod.GET) 查询单个 http://localhost:8080/employee/list?id=1 http://localhost:8080/employees/1 @RequestMapping(value = “/employees/{id}”, method = RequestMethod.GET) @ResponseBody public Employee queryById(@PathVariable Long id) {}

【2】添加

添加 传统 REST REST 后台接收 添加 http://localhost:8080/employee/add http://localhost:8080/employees @RequestMapping(value = “/employees”, method = RequestMethod.POST) public Employee add(@ModelAttribute(“emp”) Employee employee) {}

【3】修改

修改 传统 REST REST 后台接收 修改 http://localhost:8080/employee/update http://localhost:8080/employees @RequestMapping(value = “/employees”, method = RequestMethod.PUT) public Employee update(@ModelAttribute(“emp”) Employee employee) {}

【4】删除

查询 传统 REST REST 后台接收 删除 http://localhost:8080/employee/delete http://localhost:8080//employees/{id} @RequestMapping(value = “/employees/{id}”, method = RequestMethod.DELETE) @ResponseBody public JsonResult delete(@PathVariable Long id) {}

注意: 【1】当参数非常多的时候,不建议使用参数路径方式; 【2】如果参数名非常敏感,建议使用参数路径方式,可以隐藏参数名。

相关注解

@RestController 由 @Controller + @ResponseBody组成(返回 JSON 数据格式) @PathVariable URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到控制器处理方法的形参中 @RequestMapping 注解用于请求地址的解析,是最常用的一种注解 @GetMapping 查询请求 @PostMapping 添加请求 @PutMapping 更新请求 @DeleteMapping 删除请求 @RequestParam 将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)

@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)

value:参数名

required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。

defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值

HTTP响应状态码

200 OK - [GET] 服务器成功返回用户请求的数据 201 CREATED - [POST/PUT/PATCH] 用户新建或修改数据成功 202 Accepted 表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE] 用户删除数据成功 400 INVALID REQUEST - [POST/PUT/PATCH] 用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的 401 Unauthorized - [*] 表示用户没有权限(令牌、用户名、密码错误) 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的 404 NOT FOUND - [*] 用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的 405 Method Not Allowed 方法不允许,服务器没有该方法 406 Not Acceptable - [GET] 用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式) 410 Gone -[GET] 用户请求的资源被永久删除,且不会再得到的 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误 500 INTERNAL SERVER ERROR - [*] 服务器发生错误,用户将无法判断发出的请求是否成功 Spring MVC 统一异常处理 全局处理 也有两种方式 实现HandlerExceptionResolver接口,自定义异常处理器。使用HandlerExceptionResolver接口的子类,也就是SpringMVC提供的异常处理器。 springmvc 提供的 DefaultHandlerExceptionResolver,默认的异常处理器。根据各个不同类型的异常,返回不同的异常视图。SimpleMappingExceptionResolver,简单映射异常处理器。通过配置异常类和view的关系来解析异常。ResponseStatusExceptionResolver,状态码异常处理器。解析带有@ResponseStatus注释类型的异常。ExceptionHandlerExceptionResolver,注解形式的异常处理器。对@ExceptionHandler注解的方法进行异常解析。 自定义的

自定义异常类

package com.kafamiao.myexception; /** * 自定义的异常类 * @author Administrator * */ public class CustcomException extends RuntimeException{ private String message; public CustcomException(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }

实现 HandlerExceptionResolver接口定义一个异常处理器

@Component public class CustcomExceptionResolver implements HandlerExceptionResolver{ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //声明自定义异常 CustcomException custException = null; if(ex!=null && ex instanceof CustcomException) { custException = (CustcomException) ex; } else { custException = new CustcomException(ex.getMessage()); } //转向出错页面 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", custException.getMessage()); modelAndView.setViewName("err/error"); return modelAndView; } }

测试

@RequestMapping("/findall") public String findAll(Model model) { if(0==0) { throw new CustcomException("程序员主动抛出异常,查询数据出错了。"); } //用业务类调用mapper List goodslist= service.findAll(); model.addAttribute("goodslist", goodslist); return "goods/findall"; }

异常现实页

Insert title here 出错原因:${message} 注解

@ExceptionHandler注解用来将一个方法标注为异常处理方法。该注解中只有一个可选的属性value,是一个Class数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。被该注解修饰的方法的返回值为异常处理后的跳转页面,其返回值可以是ModelAndView、String,或void;方法名随意,方法的参数可以是 Exception 及其子类对象、Model、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。

@ExceptionHandler注解处理异常的作用域:单个类,只针对当前Controller。

/** * 基于注解的异常处理 */ @Controller public class ExceptionController { @RequestMapping(value = "/exception1") public String exception1() { // 模拟出现异常 System.out.println(10 / 0); return "success"; } @RequestMapping(value = "/exception2") public void exception2() throws CustomException { // 模拟出现异常 throw new CustomException("我抛出了一个异常!!!"); } //处理自定义异常 @ExceptionHandler({CustomException.class, ArithmeticException.class}) public String exceptionHandler1(Exception e, Model model) { // 打印错误信息 System.out.println(e.getMessage()); e.printStackTrace(); // 将错误数据存入请求域 model.addAttribute("exception", e); return "show-annotation-message"; } }

注意:如果在Controller中单独使用这个注解是有缺陷的,就是不能够全局处理异常,因为进行异常处理的方法必须与出错的方法在同一个Controller里面,也就是说每个Controller类中都要写一遍,所以实用性不高。

解决方案:可以将处理异常的信息抽取出来放在一个BaseController,然后对需要处理异常的Controller继承该类即可。

public class BaseController { //处理自定义异常 @ExceptionHandler({CustomException.class, ArithmeticException.class}) public String exceptionHandler1(Exception e, Model model) { // 打印错误信息 System.out.println(e.getMessage()); e.printStackTrace(); // 将错误数据存入请求域 model.addAttribute("exception", e); return "show-annotation-message"; } } 但是还是存在同样的问题,每个类都得继承它,可见这种方式同样不可取,所以一般使用下面这种方式:@ControllerAdvice和@ExceptionHandle 注解配合使用。 用 @ControllerAdvice+@ ExceptionHandler 注解(推荐)

@ExceptionHandler注解标注的异常处理方法必须与出错的方法在同一个Controller里面,所以这种方式是只对应单个Controller类。那么此时有一种更好的解决方案:可以使用@ControllerAdvice+@ExceptionHandler注解来解决,这个是 Spring 3.2 带来的新特性。

​ 两者一起使用的作用域:全局异常处理,针对全部Controller中的指定异常类

/** * 基于注解的异常处理 @ControllerAdvice+@ExceptionHandler */ // 这个注解表示当前类是一个异常映射类 @ControllerAdvice public class MyException { // 在@ExceptionHandler注解中指定异常类型 @ExceptionHandler(value = {CustomException.class, ArithmeticException.class}) public ModelAndView exceptionMapping(Exception exception) {// 方法形参位置接收SpringMVC捕获到的异常对象 // 可以将异常对象存入模型;将展示异常信息的视图设置为逻辑视图名称 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exception", exception); modelAndView.setViewName("show-annotation-message"); // 打印一下信息 System.out.println(exception.getMessage()); return modelAndView; } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有