Возврат унифицированного интерфейса SpringBoot и глобальная обработка исключений, как играют большие парни

Java задняя часть

В настоящее время большинство фреймворков проектов компаний в основном относятся к режиму разделения внешнего и внутреннего интерфейса. Этот режим будет включать проблему стыковки внешнего и внутреннего интерфейса. Будь то для внешних или внутренних служб, это очень важно. необходимо поддерживать набор полных и стандартизированных интерфейсов.这样不仅能够提高对接效率,也可以让我的代码看起来更加简洁优雅.

Самая большая разница до и после модификации заключается в том, что нам не нужно захватывать исключения в каждом интерфейсе отдельно, а также нам не нужно собирать возвращаемые параметры в каждом интерфейсе.Вы можете обратиться к следующей сравнительной таблице:

image.png

1. SpringBoot не использует унифицированный формат возврата

По умолчанию SpringBoot будет иметь следующие три ситуации возврата.

1.1 Использование возврата строки

@GetMapping("/getUserName")
public String getUserName(){
    return "HuaGe";
}

Вызов интерфейса возвращает результат:

HuaGe

1.2 Возврат с использованием класса сущности

@GetMapping("/getUserName")
public User getUserName(){
    return new User("HuaGe",18,"男");
}

Вызов интерфейса возвращает результат:

{
  "name": "HuaGe",
  "age": "18",
  "性别": "男", 
}

1.3 Возвращение в исключительных обстоятельствах

@GetMapping("/getUserName")
public static String getUserName(){
    HashMap hashMap = Maps.newHashMap();
    return hashMap.get(0).toString();
}

Имитация исключения нулевого указателя без какой-либо обработки исключений, как вы можете видетьРезультат возврата SpringBoot по умолчанию:

{
    "timestamp": "2021-08-09T06:56:41.524+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "path": "/sysUser/getUserName"
}

Для вышеуказанных случаев, если весь проект не определяет единый формат возврата, пять фоновых разработчиков определяют пять форматов возврата,这样不仅代码臃肿,前后端对接效率低, и будут некоторые неожиданные ситуации, такие как интерфейс, напрямую отображающий ненормальные детали и т. д., что дает пользователю очень плохой опыт.

2. Базовый геймплей

Чаще всего в проекте используется инкапсуляция класса инструмента, который определяет возвращаемую информацию о поле, и инкапсулирует информацию об интерфейсе, которая должна быть возвращена во внешний интерфейс через этот класс, так что феномен несогласованных форматов возврата может быть устранен. решить.

2.1 Описание параметров

  • код:Код состояния, фон может поддерживать единый набор кодов состояния;
  • сообщение:Информация описания, оперативная информация об успехе/неуспехе вызова интерфейса;
  • данные:Возврат данных.

2.2 Описание процесса

  • Создайте новый класс результатов
public class Result<T> {
    
    private int code;
    
    private String message;
    
    private T data;
​
    public Result() {}
    public Result(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    /**
     * 成功
     */
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<T>();
        result.setCode(ResultMsgEnum.SUCCESS.getCode());
        result.setMessage(ResultMsgEnum.SUCCESS.getMessage());
        result.setData(data);
        return result;
    }
​
    /**
     * 失败
     */
    public static <T> Result<T> error(int code, String message) {
        return new Result(code, message);
    }
}
  • Определите код состояния возврата
public enum ResultMsgEnum {
    SUCCESS(0, "成功"),
    FAIL(-1, "失败"),
    AUTH_ERROR(502, "授权失败!"),
    SERVER_BUSY(503, "服务器正忙,请稍后再试!"),
    DATABASE_OPERATION_FAILED(504, "数据库操作失败");
    private int code;
    private String message;
​
    ResultMsgEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
    public int getCode() {
        return this.code;
    }
    
    public String getMessage() {
        return this.message;
    }
}
  • Как пользоваться

Вышеуказанные два шага определяют数据返回格式и状态码, следующий шаг — посмотреть, как использовать его в интерфейсе.

@GetMapping("/getUserName")
public Result getUserName(){
    return Result.success("huage");
}

Результат вызова выглядит следующим образом, вы можете видеть, что это тип параметра, который мы определили в Result.

{
    "code": 0,
    "message": "成功",
    "data": "huage"
}

Хотя написание таким образом может удовлетворить повседневные потребности, и я считаю, что многие мелкие партнеры также используют это, но если у нас есть большое количество интерфейсов, то используйте их в каждом интерфейсе.Result.successЧтобы обернуть возвращаемую информацию, будет добавлено много повторяющегося кода, который недостаточно элегантен, да и показывать его стыдно. Должен быть способ снова улучшить качество кода и добиться оптимального решения.

3. Расширенное использование

Изучив базовое использование, давайте посмотрим на окончательную версию, которая в основном использует следующие два пункта знаний.Использование простое, независимо от того, используется ли оно для обучения девочек или для того, чтобы давать указания девочкам, все они являются необходимыми навыками.

3.1 Введение в класс

  • ResponseBodyAdvice:Этот интерфейс предоставляется SpringMVC 4.1, что позволяет выполнять@ResponseBodyЗатем настройте возвращаемые данные, чтобы инкапсулировать возврат в унифицированном формате данных;
  • @RestControllerAdvice:Эта аннотация расширяет возможности контроллера и может глобально перехватывать возникающие исключения.

3.2 Инструкции по применению

  • новыйResponseAdviceсвоего рода;
  • выполнитьResponseBodyAdviceинтерфейс, реализацияsupports,beforeBodyWriteметод;
  • Этот класс используется для унифицированной инкапсуляции возвращаемого результата интерфейса в контроллере.
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper;
​
    /**
     * 是否开启功能 true:是 
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
​
    /**
     * 处理返回结果
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //处理字符串类型数据
        if(o instanceof String){
            try {
                return objectMapper.writeValueAsString(Result.success(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return Result.success(o);
    }
}
​

мы можем пройтиgetUserNameПротестируйте интерфейс, вы найдете и будете использовать его напрямуюResultВозвращаемые результаты согласуются.

Однако внимательные друзья должны были заметить, что вResponseAdviceмы все использовалиResult.success(o)Для обработки результата результат типа ошибки не обрабатывается. Давайте посмотрим, каков результат возврата при возникновении исключения? Продолжайте использовать код для приведенного выше исключения нулевого указателя HashMap, и результаты теста будут следующими:

{
    "code": 0,
    "message": "成功",
    "data": {
        "timestamp": "2021-08-09T09:33:26.805+00:00",
        "status": 405,
        "error": "Method Not Allowed",
        "path": "/sysUser/getUserName"
    }
}

Хотя в формате нет проблем, он недружелюбен или неверен в конкретных данных кода и полей данных. Если вы не справитесь с этими вещами, это серьезно повлияет на ваш высокий имидж в сердце фронтенд-сестры, что абсолютно недопустимо.

3.3 Глобальный обработчик исключений

В прошлом, когда мы сталкивались с исключением, первое, что пришло нам в голову, это try..catch..finnal, но этот метод приведет к большому количеству дублирования кода, трудностям в обслуживании и раздутой логике. результат, который мы хотим.

Глобальный метод обработки исключений, который мы собираемся использовать сегодня, относительно прост в использовании. Сначала добавьте класс, добавьте@RestControllerAdviceАннотация, функция этой аннотации была представлена ​​выше, поэтому я не буду больше говорить об этом.

@RestControllerAdvice
public class CustomerExceptionHandler {
    
}

Если у нас есть тип исключения, который мы хотим перехватить, добавьте новый метод и используйте@ExceptionHandlerМодификация аннотации, параметр аннотации является целевым типом исключения.

Например: Когда в интерфейсе контроллера возникает Исключение, оно войдет вExecptionМетод перехватывается, а беспорядочная информация об исключении преобразуется в указанный формат и отправляется вResponseAdviceМетод инкапсулируется в унифицированном формате и возвращается внешнему партнеру.

@RestControllerAdvice
@Slf4j
public class CustomerExceptionHandler {
​
    @ExceptionHandler(AuthException.class)
    public String ErrorHandler(AuthorizationException e) {
        log.error("没有通过权限验证!", e);
        return "没有通过权限验证!";
    }
​
    @ExceptionHandler(Exception.class)
    public Result Execption(Exception e) {
        log.error("未知异常!", e);
        return Result.error(ResultMsgEnum.SERVER_BUSY.getCode(),ResultMsgEnum.SERVER_BUSY.getMessage());
    }
}

снова вызвать интерфейсgetUserNameГлядя на возвращенные результаты, вы обнаружите, что есть еще некоторые проблемы, потому что мы находимся вCustomerExceptionHandlerРезультат возврата интерфейса был инкапсулирован вResultтип, и код выполняется в унифицированный класс возврата результатаResponseAdviceПри повторной упаковке результата возникают следующие проблемы.

{
    "code": 0,
    "message": "成功",
    "data": {
        "code": 503,
        "message": "服务器正忙,请稍后再试!",
        "data": null
    }
}

3.4 Окончательная версия унифицированного класса обработки результатов возврата

Решить вышеуказанные проблемы очень просто, еслиbeforeBodyWriteПросто добавьте приговор.

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper;
​
    /**
     * 是否开启功能 true:开启
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
​
    /**
     * 处理返回结果
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //处理字符串类型数据
        if(o instanceof String){
            try {
                return objectMapper.writeValueAsString(Result.success(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        //返回类型是否已经封装
        if(o instanceof Result){
            return o;
        }
        return Result.success(o);
    }
}
​

На этом все задачи в этой главе завершены.На приведенный выше код можно ссылаться напрямую без других элементов конфигурации.Настоятельно рекомендуется сослаться на свой собственный проект.

4. Резюме

В этой главе объяснено не так много контента, в основном о конфигурации двух классов, то есть класса, который обрабатывает унифицированный возвращаемый результат.ResponseAdviceи класс обработки исключенийCustomerExceptionHandler. Полный текст вокругRestControllerAdvice,ResponseBodyAdviceИспользуйте, шаг за шагом, итерацию, наконец, создайте набор общего формата возврата кода кода. Эта статья относится к обмену практическими знаниями, я не знаю, есть ли другие полезные решения для унифицированной обработки для моих друзей, если есть лучшее решение, я могу поделиться им со всеми, чтобы учиться вместе.