前言:
在日常前后端分离的接口开发过程中,需要我们按相应的格式给前端返回响应的数据,常见的方式就是我们后端自己封装一个包装类,每次返回给前端数据的时候都需要我们自己手动构建一。
短时间内来看或许并没有什么,但是一旦接口量变大,我们每个接口都去构建返回值的话,那样就会浪费我们很多的开发时间,所以我们就可以对响应的内容进行统一的处理,在写Controller中的方法时我们也可以不用统一返回类型了。例如:
@ApiOperation(value = "分页查询告警信息") @PostMapping("findAlarmByPage") public PageInfo<TowerAlarmInfo> selectAll(@RequestBody AlarmReqVo alarmReqVo) { return towerAlarmInfoService.findByPage(alarmReqVo); }
这样可以让我们减少很多的工作量。 可能有的朋友就会问:那我们的非空验证可一些异常的处理怎么办呢? 下面介绍的就是统一的异常类型管理。把我们后端所有能发生的异常进行统一的封装(可以自定义一个异常类型),封装之后再返回给前端相应的提示,那么前端就会清晰的知道后端发生了什么错误,是不是前端的锅哈哈哈哈。
响应实体的封装:
首先肯定我们还是需要建一个统一返回的类:
1 package com.dlxx.tower.app.util; 2 3 import com.fasterxml.jackson.annotation.JsonInclude; 4 import lombok.AllArgsConstructor; 5 import lombok.Data; 6 import lombok.NoArgsConstructor; 7 8 @Data 9 @AllArgsConstructor 10 @NoArgsConstructor 11 //这个注解表示变量为空的时候构造json就不带上这个变量 12 @JsonInclude(JsonInclude.Include.NON_NULL) 13 public class FrontResult { 14 /** 15 * 结果状态码 16 */ 17 private Integer code; 18 /** 19 * 响应结果描述 20 */ 21 private String message; 22 /** 23 * 返回数据 24 */ 25 private Object data; 26 27 public FrontResult(Object data) { 28 this.data = data; 29 this.code = ResultEnum.SUCCESS.getCode(); 30 this.message = "操作成功"; 31 } 32 33 /** 34 * 静态方法,返回前端实体结果 35 * 36 * @param code 状态码 37 * @param message 消息 38 * @param data 数据 39 * @return 前端实体结果 40 */ 41 public static FrontResult build(Integer code, String message, Object data) { 42 return new FrontResult(code, message, data); 43 } 44 45 /** 46 * 返回成功的结果实体 47 * 48 * @param message 消息 49 * @param data 数据 50 * @return 实体 51 */ 52 public static FrontResult getSuccessResult(String message, Object data) { 53 FrontResult result = new FrontResult(); 54 result.code = ResultEnum.SUCCESS.getCode(); 55 result.message = message; 56 result.data = data; 57 return result; 58 } 59 60 /** 61 * 返回无需data的成功结果实体 62 * 63 * @param message 消息内容 64 * @return 返回结果 65 */ 66 public static FrontResult getSuccessResultOnlyMessage(String message) { 67 FrontResult result = new FrontResult(); 68 result.code = ResultEnum.SUCCESS.getCode(); 69 result.message = message; 70 result.data = null; 71 return result; 72 } 73 74 /** 75 * 获取一个异常结果 76 * 77 * @param code 错误码 78 * @param message 自定义异常信息 79 * @return FrontResult 80 */ 81 public static FrontResult getExceptionResult(Integer code, String message) { 82 FrontResult result = new FrontResult(); 83 result.code = (code == null) ? ResultEnum.CODE_EXCEPTION.getCode() : code; 84 result.message = message.isEmpty() ? ResultEnum.CODE_EXCEPTION.getMsg() : message; 85 return result; 86 } 87 88 /** 89 * 得到异常结果 90 * 91 * @param resultEnum 枚举结果代码 92 * @return {@link FrontResult} 93 */ 94 public static FrontResult getExceptionResult(ResultEnum resultEnum) { 95 FrontResult result = new FrontResult(); 96 Integer code = resultEnum.getCode(); 97 String msg = resultEnum.getMsg(); 98 result.code = (code == null) ? ResultEnum.CODE_EXCEPTION.getCode() : code; 99 result.message = msg.isEmpty() ? ResultEnum.CODE_EXCEPTION.getMsg() : msg; 100 return result; 101 } 102 }
统一封装一下枚举类型ResultEnum
import lombok.AllArgsConstructor; /** * 结果枚举 * * @author longjun * @date 2023/04/11 */ @AllArgsConstructor public enum ResultEnum { /** * 成功 */ SUCCESS(200, "操作成功"), /** * 代码异常 */ CODE_EXCEPTION(500, "后端代码内部异常"), /** * 参数错误 */ PARAMETER_ERROR(999, "前端入参异常"), /** * 失败 */ FAIL(1111, "后端代码异常异常"), /** * 空点 */ NULL_POINT(1000, "空指针异常"), /** * 指数误差 */ OUT_OF_INDEX_ERROR(1001, "索引越界异常"), /** * 模型零 */ MODEL_NULL(1002, "前端入参实体的实体为空"), /** * 数据库错误 */ DATABASE_ERROR(1003, "数据库异常"), /** * 身份验证错误 */ AUTHENTICATION_ERROR(1004, "身份验证异常"), /** * 逻辑错误 */ LOGIC_ERROR(1005, "业务逻辑异常"), /** * 类没有找到 */ CLASS_NOT_FOUND(1006, "类未找到异常"), /** * sql异常 */ SQL_EXCEPTION(1007, "sql语句异常"), /** * io例外 */ IO_EXCEPTION(1008, "io异常"), /** * json解析错误 */ JSON_PARSE_ERROR(1009, "json转换异常"), NUMBER_FORMAT_ERROR(1010, "String转换为数字错误"), /** * 更新失败 */ UPDATE_FAIL(1011, "更新失败"), /** * 发送POST错误 */ SEND_POST_ERROR(1012, "发送POST请求异常"), /** * 短信发送错误 */ SMS_SEND_ERROR(1013, "短信发送失败"); /** * 状态码 */ private Integer code; public Integer getCode() { return code; } ResultEnum(Integer code) { this.code = code; } private String msg; public String getMsg() { return msg; } }
基本的类型封装好了,接下来就是重头戏,关键代码也在这里。主要就是实现了ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
其实也就是采用了AOP的思想,对返回值进行一次修改。
该接口一共有两个方法:
(1)supports —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行 —— 通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。
(2)beforeBodyWrite —— 对 response 处理的具体执行方法。
1 package com.dlxx.tower.app.config; 2 3 import com.dlxx.tower.app.exception.BizException; 4 import com.dlxx.tower.app.util.FrontResult; 5 import com.fasterxml.jackson.core.JsonProcessingException; 6 import com.fasterxml.jackson.databind.ObjectMapper; 7 import org.springframework.core.MethodParameter; 8 import org.springframework.http.MediaType; 9 import org.springframework.http.converter.HttpMessageConverter; 10 import org.springframework.http.server.ServerHttpRequest; 11 import org.springframework.http.server.ServerHttpResponse; 12 import org.springframework.web.bind.annotation.RestControllerAdvice; 13 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 14 15 /** 16 * 响应控制器建议 17 * 18 * @author longjun 19 * @description: 全局处理增强版Controller,避免Controller里返回数据每次都要用响应体来包装 20 * 21 * @date 2023/04/04 22 */ 23 @RestControllerAdvice(basePackages = {"com.dlxx.tower.app.controller"}) 24 public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> { 25 @Override 26 public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) { 27 // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false 28 return !returnType.getGenericParameterType().equals(FrontResult.class); 29 } 30 31 @Override 32 public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { 33 // String类型不能直接包装,所以要进行些特别的处理 34 if (returnType.getGenericParameterType().equals(String.class)) { 35 ObjectMapper objectMapper = new ObjectMapper(); 36 try { 37 // 将数据包装在ResultVO里后,再转换为json字符串响应给前端 38 return objectMapper.writeValueAsString(new FrontResult(data)); 39 } catch (JsonProcessingException e) { 40 throw new BizException(); 41 } 42 } 43 // 将原本的数据包装在ResultVO里 44 return new FrontResult(data); 45 } 46 }View Code
这样基本上就可以对返回值进行统一封装了,下面就开始介绍全局异常;
统一异常处理:
首先自定义一个异常类型
1 package com.dlxx.tower.app.exception; 2 3 import com.dlxx.tower.app.util.FrontResult; 4 import com.dlxx.tower.app.util.ResultEnum; 5 6 /** 7 * 业务异常 8 * 自定义一个异常类,用于处理我们发生的业务异常 9 * 10 * @author longjun 11 * @version 1.0.0 12 * @date 2023/04/04 13 */ 14 public class BizException extends RuntimeException { 15 16 private static final long serialVersionUID = 1L; 17 18 /** 19 * 错误码 20 */ 21 protected Integer errorCode; 22 /** 23 * 错误信息 24 */ 25 protected String errorMsg; 26 27 public BizException() { 28 super(); 29 } 30 31 public BizException(FrontResult errorInfoInterface) { 32 super(errorInfoInterface.getCode().toString()); 33 this.errorCode = errorInfoInterface.getCode(); 34 this.errorMsg = errorInfoInterface.getMessage(); 35 } 36 37 public BizException(FrontResult errorInfoInterface, Throwable cause) { 38 super(errorInfoInterface.getCode().toString(), cause); 39 this.errorCode = errorInfoInterface.getCode(); 40 this.errorMsg = errorInfoInterface.getMessage(); 41 } 42 43 public BizException(String errorMsg) { 44 super(errorMsg); 45 this.errorMsg = errorMsg; 46 } 47 48 public BizException(Integer errorCode, String errorMsg) { 49 super(String.valueOf(errorCode)); 50 this.errorCode = errorCode; 51 this.errorMsg = errorMsg; 52 } 53 54 public BizException(ResultEnum resultEnum) { 55 super(String.valueOf(resultEnum.getCode())); 56 this.errorCode = resultEnum.getCode(); 57 this.errorMsg = resultEnum.getMsg(); 58 } 59 60 public BizException(Integer errorCode, String errorMsg, Throwable cause) { 61 super(String.valueOf(errorCode), cause); 62 this.errorCode = errorCode; 63 this.errorMsg = errorMsg; 64 } 65 66 67 public Integer getErrorCode() { 68 return errorCode; 69 } 70 71 public void setErrorCode(Integer errorCode) { 72 this.errorCode = errorCode; 73 } 74 75 public String getErrorMsg() { 76 return errorMsg; 77 } 78 79 public void setErrorMsg(String errorMsg) { 80 this.errorMsg = errorMsg; 81 } 82 83 @Override 84 public String getMessage() { 85 return errorMsg; 86 } 87 88 @Override 89 public Throwable fillInStackTrace() { 90 return this; 91 } 92 93 }View Code
然后就是异常处理类
1 package com.dlxx.tower.app.config; 2 3 import com.dlxx.tower.app.exception.BizException; 4 import com.dlxx.tower.app.util.FrontResult; 5 import com.dlxx.tower.app.util.ResultEnum; 6 import com.fasterxml.jackson.core.JsonParseException; 7 import lombok.extern.slf4j.Slf4j; 8 import org.springframework.http.converter.HttpMessageNotReadableException; 9 import org.springframework.web.bind.annotation.ExceptionHandler; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 import org.springframework.web.bind.annotation.RestControllerAdvice; 12 13 import javax.servlet.http.HttpServletRequest; 14 import java.io.IOException; 15 import java.sql.SQLException; 16 17 /** 18 * 全局异常处理程序 19 * <p> 20 * 统一异常处理 21 * 使用该注解表示开启了全局异常的捕获 22 * 23 * @author longjun 24 * @version 1.0.0 25 * @date 2023/04/04 26 */ 27 @RestControllerAdvice 28 @Slf4j 29 public class GlobalExceptionHandler { 30 31 /** 32 * 处理自定义的业务异常 33 * 34 * @param req 35 * @param e 36 * @return 37 */ 38 @ExceptionHandler(value = BizException.class) 39 @ResponseBody 40 public FrontResult bizExceptionHandler(HttpServletRequest req, BizException e) { 41 log.error("URL : " + req.getRequestURL().toString()); 42 log.error("HTTP_METHOD : " + req.getMethod()); 43 log.error("发生业务异常!原因是:{}", e.getErrorMsg()); 44 return FrontResult.getExceptionResult(e.getErrorCode(), e.getErrorMsg()); 45 } 46 47 /** 48 * 处理空指针的异常 49 * 50 * @param req 51 * @param e 52 * @return 53 */ 54 @ExceptionHandler(value = NullPointerException.class) 55 @ResponseBody 56 public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e) { 57 log.error("URL : " + req.getRequestURL().toString()); 58 log.error("HTTP_METHOD : " + req.getMethod()); 59 log.error("发生空指针异常!原因是:", e); 60 return FrontResult.getExceptionResult(ResultEnum.NULL_POINT); 61 } 62 63 64 /** 65 * 处理索引越界异常 66 * 67 * @param req 68 * @param e 69 * @return 70 */ 71 @ExceptionHandler(value = IndexOutOfBoundsException.class) 72 @ResponseBody 73 public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e) { 74 log.error("URL : " + req.getRequestURL().toString()); 75 log.error("HTTP_METHOD : " + req.getMethod()); 76 log.error("索引越界异常!原因是:", e); 77 return FrontResult.getExceptionResult(ResultEnum.OUT_OF_INDEX_ERROR); 78 } 79 80 /** 81 * 处理类未找到异常 82 * 83 * @param req 84 * @param e 85 * @return 86 */ 87 @ExceptionHandler(value = ClassNotFoundException.class) 88 @ResponseBody 89 public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e) { 90 log.error("URL : " + req.getRequestURL().toString()); 91 log.error("HTTP_METHOD : " + req.getMethod()); 92 log.error("发生类未找到异常!原因是:", e); 93 return FrontResult.getExceptionResult(ResultEnum.CLASS_NOT_FOUND); 94 } 95 96 97 /** 98 * 处理SQL异常 99 * 100 * @param req 101 * @param e 102 * @return 103 */ 104 @ExceptionHandler(value = SQLException.class) 105 @ResponseBody 106 public FrontResult exceptionHandler(HttpServletRequest req, SQLException e) { 107 log.error("URL : " + req.getRequestURL().toString()); 108 log.error("HTTP_METHOD : " + req.getMethod()); 109 log.error("发生SQL异常!原因是:", e); 110 return FrontResult.getExceptionResult(ResultEnum.SQL_EXCEPTION); 111 } 112 113 /** 114 * 处理IO异常 115 * 116 * @param req 117 * @param e 118 * @return 119 */ 120 @ExceptionHandler(value = IOException.class) 121 @ResponseBody 122 public FrontResult exceptionHandler(HttpServletRequest req, IOException e) { 123 log.error("URL : " + req.getRequestURL().toString()); 124 log.error("HTTP_METHOD : " + req.getMethod()); 125 log.error("发生IO异常!原因是:", e); 126 return FrontResult.getExceptionResult(ResultEnum.IO_EXCEPTION); 127 } 128 129 130 /** 131 * json转换异常处理程序 132 * 133 * @param req 要求事情 134 * @param e e 135 * @return {@link FrontResult} 136 */ 137 @ExceptionHandler(value = JsonParseException.class) 138 @ResponseBody 139 public FrontResult exceptionHandler(HttpServletRequest req, JsonParseException e) { 140 log.error("URL : " + req.getRequestURL().toString()); 141 log.error("HTTP_METHOD : " + req.getMethod()); 142 log.error("发生JSON转换异常!原因是:", e); 143 return FrontResult.getExceptionResult(ResultEnum.JSON_PARSE_ERROR); 144 } 145 146 /** 147 * String转数字异常处理程序 148 * 149 * @param req 要求事情 150 * @param e e 151 * @return {@link FrontResult} 152 */ 153 @ExceptionHandler(value = NumberFormatException.class) 154 @ResponseBody 155 public FrontResult exceptionsHandler(HttpServletRequest req, NumberFormatException e) { 156 log.error("URL : " + req.getRequestURL().toString()); 157 log.error("HTTP_METHOD : " + req.getMethod()); 158 log.error("发生String转数字异常!原因是:", e); 159 return FrontResult.getExceptionResult(ResultEnum.NUMBER_FORMAT_ERROR); 160 } 161 162 /** 163 * 前端参数不匹配异常处理程序 164 * 165 * @param req 要求事情 166 * @param e e 167 * @return {@link FrontResult} 168 */ 169 @ExceptionHandler(value = HttpMessageNotReadableException.class) 170 @ResponseBody 171 public FrontResult exceptionsHandler(HttpServletRequest req, HttpMessageNotReadableException e) { 172 log.error("URL : " + req.getRequestURL().toString()); 173 log.error("HTTP_METHOD : " + req.getMethod()); 174 log.error("发生前端参数不匹配异常!原因是:", e); 175 return FrontResult.getExceptionResult(ResultEnum.PARAMETER_ERROR); 176 } 177 178 /** 179 * 处理其他异常 180 * 181 * @param req 182 * @param e 183 * @return 184 */ 185 @ExceptionHandler(value = Exception.class) 186 @ResponseBody 187 public FrontResult exceptionHandler(HttpServletRequest req, Exception e) { 188 log.error("URL : " + req.getRequestURL().toString()); 189 log.error("HTTP_METHOD : " + req.getMethod()); 190 log.error("未知异常!原因是:", e); 191 return FrontResult.getExceptionResult(ResultEnum.FAIL); 192 } 193 194 195 }View Code
这里就已经可以实现这两个功能了
需要我们触发异常的时候就可以直接这样用
throw new BizException(ResultEnum.AUTHENTICATION_ERROR.getCode(), "验证码校验失败。");
第一个参数就是我们封装的枚举,第二个就是自定义的信息,当然也可以直接用枚举,这些都可以自己修改的。
标签:error,return,springboot,req,FrontResult,import,封装,public,统一 From: https://www.cnblogs.com/along-technology/p/17329037.html