赞
踩
为了不让后端莫名其妙的错误返回给用户,为了提高可维护性和可扩展性...为了少写两行代码,懒得打字直接搞代码
-
- import io.swagger.v3.oas.annotations.Operation;
- import io.swagger.v3.oas.annotations.tags.Tag;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.Arrays;
- import java.util.List;
-
-
- /**
- * @author: dzg
- */
- @RestController
- @RequestMapping("test")
- @Tag(name = "test控制器")
- public class Test2Controller {
-
- @GetMapping("test1")
- @Operation(summary = "无特殊返回")
- public Result<Object> test1(){
- //模拟调用方法,成功了默认返回成功,有问题直接方法直接被全局异常拦截
- test1s();
- return Result.success();
- }
-
-
- @GetMapping("test2")
- @Operation(summary = "要返回一些数据")
- public Result<List<String>> test2(){
- //模拟调用方法,成功了直接返回,有问题直接方法直接被全局异常拦截
- return Result.success(test2s());
- }
-
- @GetMapping("test3")
- @Operation(summary = "测试异常拦截")
- public Result<Object> test3(){
- //模拟调用方法,成功了直接返回,有问题直接方法直接被全局异常拦截
- return Result.success();
- }
-
- @GetMapping("test4")
- @Operation(summary = "测试异常拦截")
- public Result<Object> test4(){
- if(true){
- throw new CustomException(ResultCode.CODE_MISSING_PARAMETER);
- }
- //模拟调用方法,成功了直接返回,有问题直接方法直接被全局异常拦截
- return Result.success();
- }
- public static void test1s(){
- System.out.println(5/0);
- }
-
- public static List<String> test2s(){
- return Arrays.asList("H", "e", "l", "l", "o");
- }
- }
test1结果:
- {
- "code": 500,
- "msg": "操作失败",
- "data": null
- }
test2结果:
- {
- "code": 200,
- "msg": "操作成功",
- "data": [
- "H",
- "e",
- "l",
- "l",
- "o"
- ]
- }
这种只有一两行代码的Controller看着不清爽吗?报错也不会抛出来奇奇怪怪的东西
- public enum ResultCode {
-
- /**
- * 成功状态码
- */
- CODE_SUCCESS(200, "操作成功"),
-
- /**
- * 违规请求
- */
- CODE_METHOD_NOT_ALLOWED(405, "违规请求"),
-
- /**
- * 无效请求状态码
- */
- CODE_INVALID_REQUEST(400, "无效请求"),
-
- /**
- * 操作失败
- */
- CODE_ERROR(500, "操作失败"),
-
- /**
- * 未登录,请登录后再次访问
- */
- CODE_NOT_LOGIN(401, "未登录,请登录后再次访问");
- /**
- * 等等等...
- */
- /**
- * 状态码
- */
- private final Integer code;
-
- /**
- * msg消息
- */
- private String msg;
-
-
- ResultCode(Integer code, String msg) {
- this.code = code;
- this.msg = msg;
- }
-
- public Integer getCode() {
- return this.code;
- }
- public String getCodeStr(){
- return getCode().toString();
- }
- public String getMsg() {
- return this.msg;
- }
-
-
- }
-
- import com.dzg.demo.common.menu.ResultCode;
- import io.swagger.v3.oas.annotations.media.Schema;
-
- /**
- * @Author: dzg
- * @Date: 2023/9/18 17:14
- * @Describe:
- */
- @Schema(description = "统一出参类Result")
- public class Result<T> {
-
-
- /**
- * 状态码
- */
- @Schema(description = "状态码", defaultValue = "200")
- private int code;
- /**
- * 描述信息
- */
- @Schema(description = "描述信息", defaultValue = "操作成功")
- private String msg;
-
- /**
- * 携带出参
- */
- @Schema(description = "携带出参")
- private T data;
-
- /**
- * 返回code
- *
- * @return str
- */
- public int getCode() {
- return this.code;
- }
- public Result<T> setCode(int code) {
- this.code = code;
- return this;
- }
-
- /**
- * 给msg赋值,连缀风格
- */
- public Result<T> setMsg(String msg) {
- this.msg = msg;
- return this;
- }
-
- public String getMsg() {
- return this.msg;
- }
-
- /**
- * 给data赋值,连缀风格
- */
- public Result<T> setData(T data) {
- this.data = data;
- return this;
- }
-
- /**
- * 将data还原为指定类型并返回
- */
- public T getData() {
- return data;
- }
-
- public Result(){}
- public Result(ResultCode resultCode) {
- setCode(resultCode.getCode());
- setMsg(resultCode.getMsg());
- }
-
- public Result(int code, String msg, T data) {
- setCode(code);
- setMsg(msg);
- setData(data);
- }
-
- /**
- * 返回成功
- *
- * @return
- */
- public static <T> Result<T> success() {
- return new Result<>(ResultCode.CODE_SUCCESS);
- }
-
- /**
- * 返回成功
- *
- * @return
- */
- public static <T> Result<T> success(T data) {
- return new Result<>(ResultCode.CODE_SUCCESS.getCode(), ResultCode.CODE_SUCCESS.getMsg(), data);
- }
-
-
- /**
- * 返回失败
- *
- * @return
- */
- public static <T> Result<T> error() {
- return new Result<>(ResultCode.CODE_ERROR);
- }
- public static <T> Result<T> error(ResultCode resultCode){
- return error(resultCode.getCode(), resultCode.getMsg());
- }
-
- public static <T> Result<T> error(int code, String msg) {
- return new Result<>(code, msg, null);
- }
- }
-
- import cn.hutool.core.text.CharSequenceUtil;
- import com.dzg.demo.common.menu.ResultCode;
-
- import java.io.Serial;
-
- /**
- * @author: dzg
- * @date: 2023/9/18 17:12
- * @describe: 自定义异常类
- */
- public class CustomException extends RuntimeException {
-
- @Serial
- private static final long serialVersionUID = 6552388948084405686L;
- /**
- * 异常错误码
- */
- private int code;
-
- /**
- * 给一个空构造器
- */
- public CustomException() {
- super(ResultCode.CODE_ERROR.getMsg());
- this.code = ResultCode.CODE_ERROR.getCode();
- }
-
- public CustomException(ResultCode resultCode) {
- super(resultCode != null ? resultCode.getMsg() : ResultCode.CODE_ERROR.getMsg());
- this.code = resultCode != null ? resultCode.getCode() : ResultCode.CODE_ERROR.getCode();
- }
-
- /**
- * 自定义提示,但是通用code码为500
- * @param msg 提示
- */
- public CustomException(String msg) {
- super(msg);
- this.code = ResultCode.CODE_ERROR.getCode();
- }
- public CustomException(ResultCode resultCode, String msg) {
- super(CharSequenceUtil.format(resultCode.getMsg(), msg));
- this.code = resultCode.getCode();
- }
- /**
- * httpclient5文章中拦截responseCode使用
- */
- public CustomException(HttpStatus httpStatus) {
- super(httpStatus != null ? "远程请求失败: "+httpStatus.getReasonPhrase() : "远程请求失败: "+ResultCode.CODE_ERROR.getMsg());
- this.code = httpStatus != null ? httpStatus.value() : ResultCode.CODE_ERROR.getCode();
- }
-
- public int getCode() {
- return code;
- }
-
- public void setCode(int code) {
- this.code = code;
- }
-
- }
全局异常拦截
-
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
- import jakarta.validation.ConstraintViolationException;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.validation.FieldError;
- import org.springframework.web.HttpRequestMethodNotSupportedException;
- import org.springframework.web.bind.MethodArgumentNotValidException;
- import org.springframework.web.bind.MissingServletRequestParameterException;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ModelAttribute;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestControllerAdvice;
-
- import java.util.Objects;
-
- /**
- * @author: dzg
- * @date: 2023/9/18 17:12
- * @describe: 全局异常拦截
- */
- @RestControllerAdvice
- public class GlobalExceptionConfig {
- private static final Logger log = LoggerFactory.getLogger(GlobalExceptionConfig.class);
-
- /**
- * 在当前类每个方法进入之前触发的操作
- *
- * @param request
- */
- @ModelAttribute
- public void get(HttpServletRequest request) {
- }
-
- /**
- * 参数校验的异常(post)
- *
- * @param ex
- * @return
- */
- @ResponseBody
- @ExceptionHandler(MethodArgumentNotValidException.class)
- public Result<Object> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException ex) {
- FieldError fieldError = ex.getBindingResult().getFieldError();
- log.error("参数校验失败(POST):{}", Objects.requireNonNull(fieldError).getDefaultMessage());
- return Result.error(ResultCode.CODE_INVALID_REQUEST.getCode(), fieldError.getDefaultMessage());
- }
-
- /**
- * 参数校验的异常(get)
- *
- * @param ex
- * @return
- */
- @ResponseBody
- @ExceptionHandler(ConstraintViolationException.class)
- public Result<Object> constraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
- log.error("参数校验失败(GET):{}", ex.getMessage());
- return Result.error(ResultCode.CODE_INVALID_REQUEST.getCode(), ex.getMessage());
- }
-
- /**
- * 全局异常拦截(拦截项目中的所有异常)
- *
- * @param e
- * @param request
- * @param response
- * @return
- * @throws Exception
- */
- @ResponseBody
- @ExceptionHandler(Exception.class)
- public Result<Object> handlerException(Exception e, HttpServletRequest request, HttpServletResponse response){
- return errorHandle(request, e);
- }
-
- /**
- * 异常处理 -这里拦截后就不会继续向上抛了
- *
- * @param e
- * @return
- */
- public static Result<Object> errorHandle(HttpServletRequest request, Exception e) {
- // 输出异常防止不展示调试时困难
- e.printStackTrace();
- Result<Object> result;
- // 不同异常返回不同状态码
- if (e instanceof CustomException cx) {
- result = customExceptionHandle(cx);
- } else if (e instanceof HttpRequestMethodNotSupportedException) {
- result = Result.error(ResultCode.CODE_METHOD_NOT_ALLOWED);
- } else if (e instanceof MissingServletRequestParameterException) {
- result = Result.error(ResultCode.CODE_MISSING_PARAMETER);
- } else {
- // 返回给前端 // 普通(未知)异常 , 输出:500 + 异常信息
- result = Result.error(e.getMessage());
- }
- return result;
- }
-
- /**
- * 自定义异常处理
- *
- * @param exception
- * @return
- */
- private static Result<Object> customExceptionHandle(CustomException exception) {
- //可以处理特殊的业务
- return Result.error(exception.getCode(), exception.getMessage());
- }
-
- }
此时到这里就可以拦截任意controller异常了,但是有个问题,那就是不能拦掉过滤器中的自定义异常或者其他异常..大致思路就是创建一个过滤器优先级最高,然后捕获异常并处理。
-
- import com.alibaba.fastjson2.JSONObject;
- import jakarta.servlet.*;
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
- import org.springframework.http.HttpStatus;
- import org.springframework.web.context.support.SpringBeanAutowiringSupport;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.nio.charset.StandardCharsets;
-
-
- /**
- * @author: dzg
- * @date: 2023/9/18 23:26
- * @describe:
- */
- public class CommonFilter implements Filter {
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- // 请求进入时间
- HttpServletResponse httpServletResponse = (HttpServletResponse) response;
- HttpServletRequest httpServletRequest = (HttpServletRequest) request;
- try {
- chain.doFilter(request, httpServletResponse);
- //请求耗时
- if (HttpStatus.OK.value() != httpServletResponse.getStatus()) {
- responseError(httpServletRequest, httpServletResponse, HttpStatus.resolve(httpServletResponse.getStatus()));
- }
- } catch (Exception e) {
- //请求耗时
- Throwable error = (e.getCause() != null ? e.getCause() : e);
- error.printStackTrace();
- responseError(httpServletRequest, httpServletResponse, error);
- }
- }
-
- /**
- * 主要用来处理 @{@link GlobalExceptionConfig} 拦截不到的异常
- *
- * @param httpServletResponse
- * @param error
- * @throws IOException
- */
- public static void responseError(HttpServletRequest request, HttpServletResponse httpServletResponse, Throwable error) throws IOException {
- String errorMsg = JSONObject.toJSONString(GlobalExceptionConfig.errorHandle(request, (Exception) error));
- responseError(request, httpServletResponse, errorMsg);
- }
-
- public static void responseError(HttpServletRequest request, HttpServletResponse httpServletResponse, ResultCode resultCode) throws IOException {
- String errorMsg = JSONObject.toJSONString(Result.error(resultCode));
- responseError(request, httpServletResponse, errorMsg);
- }
-
- public static void responseError(HttpServletRequest request, HttpServletResponse httpServletResponse, HttpStatus httpStatus) throws IOException {
- String errorMsg = JSONObject.toJSONString(Result.error(httpStatus));
- responseError(request, httpServletResponse, errorMsg);
- }
-
- public static void responseError(HttpServletRequest request, HttpServletResponse httpServletResponse, String msg) throws IOException {
- httpServletResponse.setContentType("application/json; charset=utf-8");
- httpServletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
- OutputStream out = httpServletResponse.getOutputStream();
- out.write(msg.getBytes(StandardCharsets.UTF_8));
- out.flush();
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
- }
-
- @Override
- public void destroy() {
- }
-
- }
-
- import jakarta.servlet.*;
- import jakarta.servlet.http.HttpServletRequest;
-
- import java.io.IOException;
-
- /**
- * @Author: dzg
- * @Date: 2023/9/18 23:04
- * @Describe:
- */
- public class TestFilter implements Filter {
-
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- if (request.getRequestURI().contains("/test3")){
- // 测试异常
- throw new CustomException(ResultCode.CODE_CUSTOM_EXCEPTION);
- }
- filterChain.doFilter(servletRequest, servletResponse);
- }
- }
-
- import org.springframework.boot.web.servlet.FilterRegistrationBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.Ordered;
-
- /**
- * @author: dzg
- * @date: 2023/9/18 23:26
- * @describe: 注册过滤器类
- */
- @Configuration
- public class FilterRegister {
-
-
- @Bean
- public CommonFilter commonFilter(){
- return new CommonFilter();
- }
-
- /**
- * 通用过滤器---设定优先级最高
- * @return
- */
- @Bean
- public FilterRegistrationBean<CommonFilter> commonFilterRegistration() {
- FilterRegistrationBean<CommonFilter> registration = new FilterRegistrationBean<>();
- registration.setFilter(commonFilter());
- registration.setName("commonFilter");
- //此处尽量小,要比其他Filter靠前
- registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
- return registration;
- }
-
- @Bean
- public TestFilter testFilter(){
- return new TestFilter();
- }
- @Bean
- public FilterRegistrationBean<TestFilter> testFilterRegistration() {
- FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>();
- registration.setFilter(testFilter());
- registration.setName("testFilter");
- registration.setOrder(-1);
- return registration;
- }
- }
到此就结束了,过滤器拦截异常的方法其实是可以代替全局异常类且比全局异常类可拦截异常范围更广,自己选就行了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。