实际项目开发中,程序往往会发生各式各样的异常情况,特别是身为服务端开发人员的我们,总是不停的编写接口提供给前端调用,分工协作的情况下,避免不了异常的发生,如果直接将错误的信息直接暴露给用户,这样的体验可想而知,如果这接口是给第三方调用,估计别人会暴走。
- 采用try-catch的方式
笨方法不推荐使用,增大了代码量,异常过多时,很难管理业务代码和异常的匹配 - springboot提供解决方案
-
自定义异常类
@Setter @Getter @NoArgsConstructor public class AppointException extends RuntimeException { private Integer code; public AppointException(Integer code, String message) { super(message); this.setCode(code); } public AppointException(ApiConsts api) { super(api.getMsg()); this.setCode(api.getCode()); } /** * notFound * * @return */ public static AppointException notFound() { return new AppointException(ApiConsts.NOT_FOUND); } /** * 未知错误 * * @return */ public static AppointException unknown() { return new AppointException(ApiConsts.UNKNOWN); } /** * 指定code和message * * @param code * @param message * @return */ public static AppointException errorMessage(Integer code, String message) { return new AppointException(code, message); } /** * @param api * @return */ public static AppointException errorMessage(ApiConsts api) { return new AppointException(api.getCode(), api.getMsg()); } }
-
自定义异常处理器
import static org.apache.commons.lang3.StringUtils.defaultString; /** * @author: hs * @Date: 2019/5/7 17:10 * @Description: 自定义全局异常处理器。 */ @RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(Exception.class) public AbstractApiResult globalErrorHandler(Exception e, HttpServletResponse response) { logger.error("出错啦...", e); if (e instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException exec = (MethodArgumentNotValidException) e; response.setStatus(HttpStatus.BAD_REQUEST.value()); return AbstractApiResult.error(response.getStatus(), exec.getMessage()); } if (e instanceof AppointException) { AppointException exec = (AppointException) e; response.setStatus(exec.getCode()); return AbstractApiResult.error(exec.getCode(), exec.getMessage()); } if (e instanceof IllegalArgumentException) { IllegalArgumentException exec = (IllegalArgumentException) e; response.setStatus(HttpStatus.BAD_REQUEST.value()); return AbstractApiResult.error(response.getStatus(), exec.getMessage()); } if (e instanceof MultipartException) { response.setStatus(HttpStatus.BAD_REQUEST.value()); return AbstractApiResult.error(response.getStatus(), "图片大小不能超过3M"); } response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); return AbstractApiResult.error(response.getStatus(), e.getMessage()); } /** * 通用的接口映射异常处理方法 */ @Override protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { logger.error("出错啦..", ex); if (ex instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException exec = (MethodArgumentNotValidException) ex; return new ResponseEntity<>(AbstractApiResult.error(status.value(), exec.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status); } //参数转化异常 if (ex instanceof MethodArgumentTypeMismatchException) { MethodArgumentTypeMismatchException exec = (MethodArgumentTypeMismatchException) ex; logger.error("参数转换失败,方法:" + exec.getParameter().getMethod().getName() + ",参数:" + exec.getName() + ",信息:" + exec.getLocalizedMessage()); return new ResponseEntity<>(AbstractApiResult.error(ApiConsts.CONVERSION_PARAMS), status); } //请求类型异常 if (ex instanceof HttpRequestMethodNotSupportedException) { HttpRequestMethodNotSupportedException exec = (HttpRequestMethodNotSupportedException) ex; return new ResponseEntity<>(AbstractApiResult.error(status.value(), "错误的请求类型:".concat(exec.getMethod())), status); } if (ex instanceof BindException) { BindException exec = (BindException) ex; FieldError fieldError = exec.getFieldError(); //jsr303验证异常,异常堆栈信息来源于FieldError中的toString方法。 if (!Objects.equal(fieldError, null)) { String field = defaultString(fieldError.getField()); String message = defaultString(fieldError.getDefaultMessage()); return new ResponseEntity<>(AbstractApiResult.error(status.value(), field.concat(message)), status); } return new ResponseEntity<>(AbstractApiResult.error(status.value(), exec.getMessage()), status); } return new ResponseEntity<>(AbstractApiResult.error(status.value(), ex.getMessage()), status); } }
-
响应返回体
@Data public abstract class AbstractApiResult { /** * 状态码 */ protected Integer code; /** * 成功的返回. * * @param data 数据 * @return ApiResult 正常返回体 */ public static AbstractApiResult success(Object data) { return new SuccessApiResult(data); } /** * 错误返回. * * @param errorCode 错误码 * @param errorMessage 错误信息 * @return ApiResult 错误返回体 */ public static AbstractApiResult error(Integer errorCode, String errorMessage) { return new ErrorApiResult(errorCode, errorMessage); } /** * * @param api enum :包含错误码和错误信息 * @return */ public static AbstractApiResult error(ApiConsts api) { return new ErrorApiResult(api.getCode(), api.getMsg()); } }
@Data @EqualsAndHashCode(callSuper = true) public class ErrorApiResult extends AbstractApiResult { /** * 错误信息 */ private String message; ErrorApiResult(Integer code, String message) { this.code = code; this.message = message; } }
@Data @EqualsAndHashCode(callSuper = true) public class SuccessApiResult extends AbstractApiResult { /** * 响应数据 */ private Object data; SuccessApiResult(Object data) { this.code = 0; this.data = data; } }
-
辅助与model
@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public enum ApiConsts { /** * client端异常信息返回 */ SUCCESS(1, "请求成功"), UNKNOWN(-1, "未知错误"), NOT_FOUND(404, "not found"), PORTABILITY_PARAMS(1001, "携带参数错误"), CONVERSION_PARAMS(1002, "参数转换失败"), REQUEST_TYPE(405, "错误的请求类型:"); private final Integer code; private final String msg; }
@Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @ApiModel("交易记录详情") public class RecordDetails { @NotBlank private String tacheCode; @NotBlank private String processCode; @NotBlank private String senceId; private String proOrderId; private Integer numId; private Integer iccId; private String imsi; private Integer simId; @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime beginTime; @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; }
-
接口验证
@RestController @RequestMapping("/conf") @Api(description = "表格配置") public class FromHeaderController { @GetMapping("/from/{serviceCode}") @ApiOperation(value = "table配置信息", notes = "接口返回table表格的配置信息") @ApiImplicitParam(value = "服务名称", name = "serviceCode", paramType = "path") public AbstractApiResult listFromHeaders(@PathVariable(value = "serviceCode") Integer serviceCode) { if (Objects.equal(serviceCode,1)) { throw AppointException.errorMessage(ApiConsts.PORTABILITY_PARAMS); } return AbstractApiResult.success(serviceCode); } }
请求http://localhost:9001/conf/from/1,返回结果如下:
@Slf4j @RestController @RequestMapping("/details") @Api(description = "交易记录列表操作") public class RecordDetailsController { private final RecordDetailsService recordDetailsService; @Autowired public RecordDetailsController(RecordDetailsService recordDetailsService) { this.recordDetailsService = recordDetailsService; } @PostMapping(value = "/recordData", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @ApiOperation(value = "查询框选散点图信息") public AbstractApiResult listRecordDetails(@Validated @RequestBody RecordDetails recordDetails) { log.info("recordDetails:{}",recordDetails); List<RecordDetails> recordDetailses = recordDetailsService.listRecordDetails(recordDetails); return AbstractApiResult.success(null); } }
请求http://localhost:9001/details/recordData,返回结果如下: