1. Предпосылки
Сегодня, когда преобладают распределенные и микросервисы, в большинстве проектов используется среда микросервисов, которая разделяет интерфейс и серверную часть. Фронтенд и бэкенд взаимодействуют, а фронтенд запрашивает в соответствии с соглашениемURL
Path и передать соответствующие параметры, внутренний сервер получает запрос, выполняет бизнес-обработку и возвращает данные внешнему интерфейсу.
Поэтому очень важно унифицировать возвращаемое значение интерфейса и обеспечить идемпотентность возвращаемого значения интерфейса.Эта статья в основном знакомит с набором результатов, используемым в настоящее время блогерами.
2. Единый дизайн формата
2.1 Общая форма унифицированных результатов
- Пример:
{
# 是否响应成功
success: true,
# 响应状态码
code: 200,
# 响应数据
data: Object
# 返回错误信息
message: "",
}
2.2 Перечисление классов результатов
public enum ResultCodeEnum {
/*** 通用部分 100 - 599***/
// 成功请求
SUCCESS(200, "successful"),
// 重定向
REDIRECT(301, "redirect"),
// 资源未找到
NOT_FOUND(404, "not found"),
// 服务器错误
SERVER_ERROR(500,"server error"),
/*** 这里可以根据不同模块用不同的区级分开错误码,例如: ***/
// 1000~1999 区间表示用户模块错误
// 2000~2999 区间表示订单模块错误
// 3000~3999 区间表示商品模块错误
// 。。。
;
/**
* 响应状态码
*/
private Integer code;
/**
* 响应信息
*/
private String message;
ResultCodeEnum(Integer code, String msg) {
this.code = code;
this.message = msg;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
-
code
: код состояния ответа
Как правило, друзья добавляют то, что им нужно во время разработки.но, для уточнения мы должны обратиться кHTTP
Код состояния, возвращаемый запросом.
диапазон кодов | тип | значение | |
---|---|---|---|
1** | 100-199 | Информация | Сервер получает запрос и требует от запрашивающей стороны продолжить операцию. |
2** | 200-299 | успех | Запрос успешно принят и обработан |
3** | 300-399 | перенаправить | Для выполнения запроса необходимы дальнейшие действия. |
4** | 400-499 | ошибка клиента | Запрос содержит синтаксическую ошибку или запрос не может быть выполнен |
5** | 500-599 | Ошибка сервера | Произошла ошибка при обработке сервера |
ОбщийHTTP
код состояния:
-
200
- запрос выполнен успешно; -
301
- ресурсы (веб-страницы и т.д.) постоянно перемещаются в другиеURL
; -
404
- запрашиваемый ресурс (веб-страница и т.п.) не существует; -
500
- Внутренняя Ошибка Сервера.
-
message
: сообщение об ошибке
Когда возникает ошибка, как дать дружескую подсказку?
- в соответствии с
code
Укажите соответствующее местоположение кода ошибки; - записать описание ошибки в
message
, что удобно вызывающей стороне интерфейса для более подробного понимания ошибки.
2.3 Единый класс результатов
public class HttpResult <T> implements Serializable {
/**
* 是否响应成功
*/
private Boolean success;
/**
* 响应状态码
*/
private Integer code;
/**
* 响应数据
*/
private T data;
/**
* 错误信息
*/
private String message;
// 构造器开始
/**
* 无参构造器(构造器私有,外部不可以直接创建)
*/
private HttpResult() {
this.code = 200;
this.success = true;
}
/**
* 有参构造器
* @param obj
*/
private HttpResult(T obj) {
this.code = 200;
this.data = obj;
this.success = true;
}
/**
* 有参构造器
* @param resultCode
*/
private HttpResult(ResultCodeEnum resultCode) {
this.success = false;
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
// 构造器结束
/**
* 通用返回成功(没有返回结果)
* @param <T>
* @return
*/
public static<T> HttpResult<T> success(){
return new HttpResult();
}
/**
* 返回成功(有返回结果)
* @param data
* @param <T>
* @return
*/
public static<T> HttpResult<T> success(T data){
return new HttpResult<T>(data);
}
/**
* 通用返回失败
* @param resultCode
* @param <T>
* @return
*/
public static<T> HttpResult<T> failure(ResultCodeEnum resultCode){
return new HttpResult<T>(resultCode);
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "HttpResult{" +
"success=" + success +
", code=" + code +
", data=" + data +
", message='" + message + '\'' +
'}';
}
}
инструкция:
- Конструктор является закрытым и не может быть создан непосредственно внешним миром;
- Только статический метод унифицированного возвращаемого класса может быть вызван для возврата объекта;
-
success
ЯвляетсяBoolean
значение, через это значение вы можете напрямую наблюдать, успешен ли запрос; -
data
Указывает данные ответа, которые используются для возврата данных, требуемых клиентом после успешного выполнения запроса.
3. Тест и резюме
3.1 Простой тест интерфейса
@RestController
@RequestMapping("/httpRest")
@Api(tags = "统一结果测试")
public class HttpRestController {
@ApiOperation(value = "通用返回成功(没有返回结果)", httpMethod = "GET")
@GetMapping("/success")
public HttpResult success(){
return HttpResult.success();
}
@ApiOperation(value = "返回成功(有返回结果)", httpMethod = "GET")
@GetMapping("/successWithData")
public HttpResult successWithData(){
return HttpResult.success("风尘博客");
}
@ApiOperation(value = "通用返回失败", httpMethod = "GET")
@GetMapping("/failure")
public HttpResult failure(){
return HttpResult.failure(ResultCodeEnum.NOT_FOUND);
}
}
здесь
Swagger
а такжеSpringMVC
Конфигурация не была опубликована, подробности см. в образце кода Github.
3.2 Вернуть результат
http://localhost:8080/swagger-ui.html#/
{
"code": 200,
"success": true
}
{
"code": 200,
"data": "风尘博客",
"success": true
}
{
"code": 404,
"message": "not found",
"success": false
}
В-четвертых, глобальная обработка исключений
При использовании унифицированных результатов возврата возникает другая ситуация, то есть ошибка программы является результатом исключений во время выполнения, некоторые исключения выбрасываются нами в бизнесе, а некоторые нельзя предсказать заранее.
Поэтому нам нужно определить единое глобальное исключение, вController
Все исключения перехватываются, обрабатываются соответствующим образом и возвращаются в результате.
4.1 Идеи дизайна:
- Настройте класс исключения (например:
TokenVerificationException
), ловить исключения, характерные для проекта или бизнеса; - использовать
@ExceptionHandler
Аннотации фиксируют пользовательские исключения и общие исключения; - использовать
@ControllerAdvice
интегрированный@ExceptionHandler
метод в класс; - Информация об аномальном объекте добавляется к унифицированному перечислению результатов;
4.2 Пользовательские исключения
public class TokenVerificationException extends RuntimeException {
/**
* 错误码
*/
protected Integer code;
protected String msg;
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
/**
* 有参构造器,返回码在枚举类中,这里可以指定错误信息
* @param msg
*/
public TokenVerificationException(String msg) {
super(msg);
}
}
4.3 Единый обработчик исключений
@ControllerAdvice
Аннотация — это уведомление об аспекте, которое действует на уровне управления (Advice
), может преобразовать общее@ExceptionHandler
,@InitBinder
и@ModelAttributes
Методы собираются в тип и применяются ко всем контроллерам.
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常捕获
* @param e 捕获的异常
* @return 封装的返回对象
**/
@ExceptionHandler(Exception.class)
public HttpResult handlerException(Exception e) {
ResultCodeEnum resultCodeEnum;
// 自定义异常
if (e instanceof TokenVerificationException) {
resultCodeEnum = ResultCodeEnum.TOKEN_VERIFICATION_ERROR;
resultCodeEnum.setMessage(getConstraintViolationErrMsg(e));
log.error("tokenVerificationException:{}", resultCodeEnum.getMessage());
}else {
// 其他异常,当我们定义了多个异常时,这里可以增加判断和记录
resultCodeEnum = ResultCodeEnum.SERVER_ERROR;
resultCodeEnum.setMessage(e.getMessage());
log.error("common exception:{}", JSON.toJSONString(e));
}
return HttpResult.failure(resultCodeEnum);
}
/**
* 获取错误信息
* @param ex
* @return
*/
private String getConstraintViolationErrMsg(Exception ex) {
// validTest1.id: id必须为正数
// validTest1.id: id必须为正数, validTest1.name: 长度必须在有效范围内
String message = ex.getMessage();
try {
int startIdx = message.indexOf(": ");
if (startIdx < 0) {
startIdx = 0;
}
int endIdx = message.indexOf(", ");
if (endIdx < 0) {
endIdx = message.length();
}
message = message.substring(startIdx, endIdx);
return message;
} catch (Throwable throwable) {
log.info("ex caught", throwable);
return message;
}
}
}
- инструкция
- я использую
@RestControllerAdvice
, Эквивалентно@ControllerAdvice
+@ResponseBody
- Класс перечисления ошибок здесь опущен, см. подробностиКод на гитхабе.
V. Тест и резюме
5.1 Тестовый интерфейс
@RestController
@RequestMapping("/exception")
@Api(tags = "异常测试接口")
public class ExceptionRestController {
@ApiOperation(value = "业务异常(token 异常)", httpMethod = "GET")
@GetMapping("/token")
public HttpResult token() {
// 模拟业务层抛出 token 异常
throw new TokenVerificationException("token 已经过期");
}
@ApiOperation(value = "其他异常", httpMethod = "GET")
@GetMapping("/errorException")
public HttpResult errorException() {
//这里故意造成一个其他异常,并且不进行处理
Integer.parseInt("abc123");
return HttpResult.success();
}
}
5.2 Вернуть результат
http://localhost:8080/swagger-ui.html#/
{
"code": 500,
"message": "For input string: \"abc123\"",
"success": false
}
{
"code": 4000,
"message": "token 已经过期",
"success": false
}
5.3 Резюме
@RestControllerAdvice
и@ExceptionHandler
поймает всехRest
Исключение интерфейса инкапсулируется в тот, который мы определяемHttpResult
Однако возвращается результирующий набор:Не могу обрабатывать исключения в перехватчиках
6. Резюме
Нет решения, подходящего для различных ситуаций, таких как пейджинг, и можно добавить статическое решение для возврата результатов пейджинга Конкретная реализация здесь не будет показана. Поэтому для вас очень хорошо иметь определенную читабельность.Вы можете высказывать мнения и предложения от старших братьев с разными мнениями.