3.3、代码实现
3.3.1、ThreadLocal工具类
定义ThreadLocal工具类,仅需要调用set方法即可将数据存入ThreadLocal中
1. /** 2. * 工具类:实现向threadlocal存储数据的方法 3. */ 4. public class UserHolder { 5. 6. private static ThreadLocal<User> tl = new ThreadLocal<>(); 7. 8. 9. //将用户对象,存入Threadlocal 10. public static void set(User user) { 11. tl.set(user); 12. } 13. 14. //从当前线程,获取用户对象 15. public static User get() { 16. return tl.get(); 17. } 18. 19. //从当前线程,获取用户对象的id 20. public static Long getUserId() { 21. return tl.get().getId(); 22. } 23. 24. //从当前线程,获取用户对象的手机号码 25. public static String getMobile() { 26. return tl.get().getMobile(); 27. } 28. }
3.3.2、定义拦截器
定义拦截器,在前置拦截方法preHandle中解析token并验证有效性,如果失效返回状态码401。如果有效,解析User对象,存入ThreadLocal中
1. public class TokenInterceptor implements HandlerInterceptor { 2. 3. @Override 4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 5. 6. //1、获取请求头 7. String token = request.getHeader("Authorization"); 8. 9. //2、使用工具类,判断token是否有效 10. boolean verifyToken = JwtUtils.verifyToken(token); 11. //3、如果token失效,返回状态码401,拦截 12. if(!verifyToken) { 13. response.setStatus(401); 14. return false; 15. } 16. //4、如果token正常可用,放行 17. 18. //解析token,获取id和手机号码,构造User对象,存入Threadlocal 19. Claims claims = JwtUtils.getClaims(token); 20. String mobile = (String) claims.get("mobile"); 21. Integer id = (Integer) claims.get("id"); 22. 23. User user = new User(); 24. user.setId(Long.valueOf(id)); 25. user.setMobile(mobile); 26. 27. UserHolder.set(user); 28. 29. return true; 30. } 31. }
3.3.3、注册拦截器
拦截器需要注册到MVC容器中
1. @Configuration 2. public class WebConfig implements WebMvcConfigurer { 3. 4. @Override 5. public void addInterceptors(InterceptorRegistry registry) { 6. registry.addInterceptor(new TokenInterceptor()) 7. .addPathPatterns("/**") 8. .excludePathPatterns(new String[]{"/user/login","/user/loginVerification"}); 9. } 10. }
3.3.4、测试
修改UsersController方法,从ThreadLoca中获取用户数据
1. /** 2. * 查询用户资料 3. */ 4. @GetMapping 5. public ResponseEntity users(Long userID) { 6. if(userID == null) { 7. userID = UserHolder.getUserId(); 8. } 9. UserInfoVo userInfo = userInfoService.findById(userID); 10. return ResponseEntity.ok(userInfo); 11. }
4、统一异常处理
软件开发过程中,不可避免的是需要处理各种异常,常见的形式就是逐层向上抛出,web层进行处理。使用try {...} catch {...}很方便就能对异常做到业务处理
- 冗余代码多,影响代码可读性
- 异常处理和业务代码耦合
4.1、解决方案
SpringMVC提供了一套解决全局异常的处理方案,可以在代码无侵入的前提下完成异常处理。遵循逐层抛出,异常处理器统一处理的思路
4.2、代码实现
项目中可能存在不可预知的各种异常,如:空指针,数组越界等。针对这类异常,可以直接在异常处理器中统一处理;
还有一类是可预知的错误,如图片不合法,验证码错误等等。这类错误也可以理解为业务异常,可以通过自定义异常类来处理;
4.2.1、业务异常对象
为了方便操作,将一些常见的业务错误封装到ErrorResult对象中
1. @Data 2. @NoArgsConstructor 3. @AllArgsConstructor 4. @Builder 5. public class ErrorResult { 6. 7. private String errCode = "999999"; 8. private String errMessage; 9. 10. public static ErrorResult error() { 11. return ErrorResult.builder().errCode("999999").errMessage("系统异常稍后再试").build(); 12. } 13. 14. public static ErrorResult fail() { 15. return ErrorResult.builder().errCode("000001").errMessage("发送验证码失败").build(); 16. } 17. 18. public static ErrorResult loginError() { 19. return ErrorResult.builder().errCode("000002").errMessage("验证码失效").build(); 20. } 21. 22. public static ErrorResult faceError() { 23. return ErrorResult.builder().errCode("000003").errMessage("图片非人像,请重新上传!").build(); 24. } 25. 26. public static ErrorResult mobileError() { 27. return ErrorResult.builder().errCode("000004").errMessage("手机号码已注册").build(); 28. } 29. 30. public static ErrorResult contentError() { 31. return ErrorResult.builder().errCode("000005").errMessage("动态内容为空").build(); 32. } 33. 34. public static ErrorResult likeError() { 35. return ErrorResult.builder().errCode("000006").errMessage("用户已点赞").build(); 36. } 37. 38. public static ErrorResult disLikeError() { 39. return ErrorResult.builder().errCode("000007").errMessage("用户未点赞").build(); 40. } 41. 42. public static ErrorResult loveError() { 43. return ErrorResult.builder().errCode("000008").errMessage("用户已喜欢").build(); 44. } 45. 46. public static ErrorResult disloveError() { 47. return ErrorResult.builder().errCode("000009").errMessage("用户未喜欢").build(); 48. } 49. }
4.2.2 业务异常类
自定义业务异常类,针对业务错误之间抛出业务异常即可
1. /** 2. * 自定义异常类 3. */ 4. @Data 5. public class BusinessException extends RuntimeException { 6. 7. private ErrorResult errorResult; 8. 9. public BusinessException(ErrorResult errorResult) { 10. super(errorResult.getErrMessage()); 11. this.errorResult = errorResult; 12. } 13. }
4.2.3、异常处理器
1. /** 2. * 自定义统一异常处理 3. * 1、通过注解,声明异常处理类 4. * 2、编写方法,在方法内部处理异常,构造响应数据 5. * 3、方法上编写注解,指定此方法可以处理的异常类型 6. */ 7. @ControllerAdvice 8. public class ExceptionAdvice { 9. 10. //处理业务异常 11. @ExceptionHandler(BusinessException.class) 12. public ResponseEntity handlerException(BusinessException be) { 13. be.printStackTrace(); 14. ErrorResult errorResult = be.getErrorResult(); 15. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult); 16. } 17. 18. //处理不可预知的异常 19. @ExceptionHandler(Exception.class) 20. public ResponseEntity handlerException1(Exception be) { 21. be.printStackTrace(); 22. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ErrorResult.error()); 23. } 24. }