1. 统一使用ResponseEntity类 : 用于统一响应格式
使用org.springframework.http.ResponseEntity作为统一响应格式
-
示例:
@GetMapping("/as/{id}") @Timed public ResponseEntity<A> getA(@PathVariable Long id) { log.debug("REST request to get A : {}", id); A a = aService.findOne(id); return ResponseUtil.wrapOrNotFound(Optional.ofNullable(a)); }
2.统一的Error类 : 用于统一异常格式
- 统一使用org.zalando.problem.ThrowableProblem的子类作为异常格式
-
统一使用httpStatus作为错误编码
public enum Status implements StatusType { CONTINUE(100, "Continue"), SWITCHING_PROTOCOLS(101, "Switching Protocols"), PROCESSING(102, "Processing"), CHECKPOINT(103, "Checkpoint"), OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), NO_CONTENT(204, "No Content"), RESET_CONTENT(205, "Reset Content"), PARTIAL_CONTENT(206, "Partial Content"), MULTI_STATUS(207, "Multi-Status"), ALREADY_REPORTED(208, "Already Reported"), IM_USED(226, "IM Used"), MULTIPLE_CHOICES(300, "Multiple Choices"), MOVED_PERMANENTLY(301, "Moved Permanently"), FOUND(302, "Found"), SEE_OTHER(303, "See Other"), NOT_MODIFIED(304, "Not Modified"), USE_PROXY(305, "Use Proxy"), TEMPORARY_REDIRECT(307, "Temporary Redirect"), PERMANENT_REDIRECT(308, "Permanent Redirect"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, "Unauthorized"), PAYMENT_REQUIRED(402, "Payment Required"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), METHOD_NOT_ALLOWED(405, "Method Not Allowed"), NOT_ACCEPTABLE(406, "Not Acceptable"), PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), REQUEST_TIMEOUT(408, "Request Timeout"), CONFLICT(409, "Conflict"), GONE(410, "Gone"), LENGTH_REQUIRED(411, "Length Required"), PRECONDITION_FAILED(412, "Precondition Failed"), REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), EXPECTATION_FAILED(417, "Expectation Failed"), I_AM_A_TEAPOT(418, "I'm a teapot"), UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), LOCKED(423, "Locked"), FAILED_DEPENDENCY(424, "Failed Dependency"), UPGRADE_REQUIRED(426, "Upgrade Required"), PRECONDITION_REQUIRED(428, "Precondition Required"), TOO_MANY_REQUESTS(429, "Too Many Requests"), REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), INTERNAL_SERVER_ERROR(500, "Internal Server Error"), NOT_IMPLEMENTED(501, "Not Implemented"), BAD_GATEWAY(502, "Bad Gateway"), SERVICE_UNAVAILABLE(503, "Service Unavailable"), GATEWAY_TIMEOUT(504, "Gateway Timeout"), HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"), VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), INSUFFICIENT_STORAGE(507, "Insufficient Storage"), LOOP_DETECTED(508, "Loop Detected"), BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), NOT_EXTENDED(510, "Not Extended"), NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); private final int code; private final String reason; private Status(int statusCode, String reasonPhrase) { this.code = statusCode; this.reason = reasonPhrase; } public int getStatusCode() { return this.code; } public String getReasonPhrase() { return this.reason; } }
3.自定义异常 : 区分不同场景的异常
-
请求异常
public class BadRequestAlertException extends AbstractThrowableProblem{ private final String entityName; private final String errorKey; public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) { this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey); } public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) { super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)); this.entityName = entityName; this.errorKey = errorKey; } public String getEntityName() { return entityName; } public String getErrorKey() { return errorKey; } private static Map<String, Object> getAlertParameters(String entityName, String errorKey) { Map<String, Object> parameters = new HashMap<>(); parameters.put("message", "error." + errorKey); parameters.put("params", entityName); return parameters; }
}
-
参数异常
public class CustomParameterizedException extends AbstractThrowableProblem { private static final long serialVersionUID = 1L; private static final String PARAM = "param"; public CustomParameterizedException(String message, String... params) { this(message, toParamMap(params)); } public CustomParameterizedException(String message, Map<String, Object> paramMap) { super(ErrorConstants.PARAMETERIZED_TYPE, "Parameterized Exception", BAD_REQUEST, null, null, null, toProblemParameters(message, paramMap)); } public static Map<String, Object> toParamMap(String... params) { Map<String, Object> paramMap = new HashMap<>(); if (params != null && params.length > 0) { for (int i = 0; i < params.length; i++) { paramMap.put(PARAM + i, params[i]); } } return paramMap; } public static Map<String, Object> toProblemParameters(String message, Map<String, Object> paramMap) { Map<String, Object> parameters = new HashMap<>(); parameters.put("message", message); parameters.put("params", paramMap); return parameters; }
}
并发异常
org.springframework.dao.ConcurrencyFailureException其他异常
按需定义,继承org.zalando.problem.ThrowableProblem即可
4.实现ExceptionHandler : 用于拦截处理异常
@ControllerAdvice
public class ExceptionTranslator implements ProblemHandling {
/**
* Post-process Problem payload to add the message key for front-end if needed
*/
@Override
public ResponseEntity<Problem> process(@Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
if (entity == null || entity.getBody() == null) {
return entity;
}
Problem problem = entity.getBody();
if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
return entity;
}
ProblemBuilder builder = Problem.builder()
.withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
.withStatus(problem.getStatus())
.withTitle(problem.getTitle())
.with("path", request.getNativeRequest(HttpServletRequest.class).getRequestURI());
if (problem instanceof ConstraintViolationProblem) {
builder
.with("violations", ((ConstraintViolationProblem) problem).getViolations())
.with("message", ErrorConstants.ERR_VALIDATION);
return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
} else {
builder
.withCause(((DefaultProblem) problem).getCause())
.withDetail(problem.getDetail())
.withInstance(problem.getInstance());
problem.getParameters().forEach(builder::with);
if (!problem.getParameters().containsKey("message") && problem.getStatus() != null) {
builder.with("message", "error.http." + problem.getStatus().getStatusCode());
}
return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
}
}
@Override
public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
BindingResult result = ex.getBindingResult();
List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
.map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode()))
.collect(Collectors.toList());
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(defaultConstraintViolationStatus())
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", fieldErrors)
.build();
return create(ex, problem, request);
}
@ExceptionHandler(BadRequestAlertException.class)
public ResponseEntity<Problem> handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
return create(ex, request, HeaderUtil.createFailureAlert(ex.getEntityName(), ex.getErrorKey(), ex.getMessage()));
}
@ExceptionHandler(ConcurrencyFailureException.class)
public ResponseEntity<Problem> handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.withStatus(Status.CONFLICT)
.with("message", ErrorConstants.ERR_CONCURRENCY_FAILURE)
.build();
return create(ex, problem, request);
}
}
5.统一业务异常抛出
异常统一在service或者controller里面抛出,抛出异常类型为BadRequestAlertException
- 例子
@PostMapping("/as")
@Timed
public ResponseEntity<A> createA(@Valid @RequestBody A a) throws URISyntaxException {
log.debug("REST request to save A : {}", a);
if (a.getId() != null) {
throw new BadRequestAlertException("A new a cannot already have an ID", ENTITY_NAME, "idexists");
}
A result = aService.save(a);
return ResponseEntity.created(new URI("/api/as/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}