SpringBoot: как изящно передавать параметры, инкапсулировать данные ответа и обрабатывать исключения?

Spring Boot задняя часть

Автор: Юнь Шэнь я не знаю где

blog.csdn.net/mu_wind/article/details/99960645

При разработке проекта для передачи данных между интерфейсами и между интерфейсом и сервером используется формат JSON.

1 использование fastjson

Fastjson от Alibaba в настоящее время является наиболее широко используемой структурой для синтаксического анализа JSON. В этой статье также будет использоваться fastjson.

1.1 Знакомство с зависимостями

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.35</version>
</dependency>

2 Унифицированная инкапсуляция и возвращаемые данные

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

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author guozhengMu
 * @version 1.0
 * @date 2019/8/21 14:55
 * @description
 * @modify
 */
@RestController
@RequestMapping(value = "/test", method = RequestMethod.GET)
public class TestController {
    @RequestMapping("/json")
    public JSONObject test() {
        JSONObject result = new JSONObject();
        try {
            // 业务逻辑代码
            result.put("code", 0);
            result.put("msg", "操作成功!");
            result.put("data", "测试数据");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("msg", "系统异常,请联系管理员!");
        }
        return result;
    }
}

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

2.1 Определите единую структуру JSON

Атрибуты в единой структуре JSON включают данные, код состояния, подсказки и другие элементы, которые можно добавлять по мере необходимости. В общем, должна быть структура возврата по умолчанию, а также должна быть заданная пользователем структура возврата. Поскольку тип возвращаемых данных не может быть определен, необходимо использовать дженерики.Код выглядит следующим образом:

public class ResponseInfo<T> {
    /**
     * 状态码
     */
    protected String code;
    /**
     * 响应信息
     */
    protected String msg;
    /**
     * 返回数据
     */
    private T data;

    /**
     * 若没有数据返回,默认状态码为 0,提示信息为“操作成功!”
     */
    public ResponseInfo() {
        this.code = 0;
        this.msg = "操作成功!";
    }

    /**
     * 若没有数据返回,可以人为指定状态码和提示信息
     * @param code
     * @param msg
     */
    public ResponseInfo(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 有数据返回时,状态码为 0,默认提示信息为“操作成功!”
     * @param data
     */
    public ResponseInfo(T data) {
        this.data = data;
        this.code = 0;
        this.msg = "操作成功!";
    }

    /**
     * 有数据返回,状态码为 0,人为指定提示信息
     * @param data
     * @param msg
     */
    public ResponseInfo(T data, String msg) {
        this.data = data;
        this.code = 0;
        this.msg = msg;
    }
    // 省略 get 和 set 方法
}

2.2 Используйте единую структуру JSON

После того, как мы инкапсулируем унифицированную структуру возвращаемых данных, мы можем использовать ее непосредственно в интерфейсе. следующее:

import com.example.demo.model.ResponseInfo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author guozhengMu
 * @version 1.0
 * @date 2019/8/21 14:55
 * @description
 * @modify
 */
@RestController
@RequestMapping(value = "/test", method = RequestMethod.GET)
public class TestController {
    @RequestMapping("/json")
    public ResponseInfo test() {
        try {
            // 模拟异常业务代码
            int num = 1 / 0;
            return new ResponseInfo("测试数据");
        } catch (Exception e) {
            return new ResponseInfo(500, "系统异常,请联系管理员!");
        }
    }
}

Как и выше, обработка возвращаемых данных интерфейса гораздо более элегантна. Сделайте тест для вышеуказанного интерфейса, запустите проект, зайдите через браузер: localhost:8096/test/json и получите результат ответа:

{"code":500,"msg":"系统异常,请联系管理员!","data":null}

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

3.1 Системная обработка исключений

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

@RestControllerAdvice
@Slf4j
public class ExceptionHandlerAdvice {

    // 参数格式异常处理
    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInfo badRequestException(IllegalArgumentException exception) {
     log.error("参数格式不合法:" + e.getMessage());
        return new ResponseInfo(HttpStatus.BAD_REQUEST.value() + "", "参数格式不符!");
    }

 // 权限不足异常处理
    @ExceptionHandler({AccessDeniedException.class})
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ResponseInfo badRequestException(AccessDeniedException exception) {
        return new ResponseInfo(HttpStatus.FORBIDDEN.value() + "", exception.getMessage());
    }

 // 参数缺失异常处理
    @ExceptionHandler({MissingServletRequestParameterException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseInfo badRequestException(Exception exception) {
        return new ResponseInfo(HttpStatus.BAD_REQUEST.value() + "", "缺少必填参数!");
    }

    // 空指针异常
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInfo handleTypeMismatchException(NullPointerException ex) {
        log.error("空指针异常,{}", ex.getMessage());
        return new JsonResult("500", "空指针异常");
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult handleUnexpectedServer(Exception ex) {
        log.error("系统异常:", ex);
        return new JsonResult("500", "系统发生异常,请联系管理员");
    }
    
    // 系统异常处理
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseInfo exception(Throwable throwable) {
        log.error("系统异常", throwable);
        return new ResponseInfo(HttpStatus.INTERNAL_SERVER_ERROR.value() + "系统异常,请联系管理员!");
    }
}
  1. Аннотация @RestControllerAdvice содержит аннотацию @Component, указывающую, что при запуске Spring Boot класс также будет передан Spring в качестве компонента для управления.
  2. Аннотация @RestControllerAdvice содержит аннотацию @ResponseBody для вывода данных пакета в формате JSON вызывающему объекту после обработки исключения.
  3. В аннотации @RestControllerAdvice также есть атрибут basePackages, который используется для перехвата информации об исключениях, в каком пакете.Как правило, мы не указываем этот атрибут и перехватываем все исключения в проекте.
  4. Конкретное исключение указывается в аннотации @ExceptionHandler к методу, затем информация об исключении обрабатывается в методе, и, наконец, результат возвращается вызывающему объекту через унифицированную структуру JSON.
  5. Однако в проекте мы, как правило, перехватываем некоторые распространенные исключения более подробно.Хотя перехват Exception может быть выполнен раз и навсегда, это не способствует устранению неполадок или обнаружению проблем. В реальном проекте вы можете написать перехваченное исключение Exception в нижней части GlobalExceptionHandler.Если оно не найдено, окончательно перехватите исключение Exception, чтобы убедиться, что выводимая информация понятна.

Давайте проверим это через интерфейс:

@RestController
@RequestMapping(value = "/test", method = RequestMethod.POST)
public class TestController {
    @RequestMapping("/json")
    public ResponseInfo test(@RequestParam String userName, @RequestParam String password) {
        try {
            String data = "登录用户:" + userName + ",密码:" + password;
            return new ResponseInfo("0", "操作成功!", data);
        } catch (Exception e) {
            return new ResponseInfo("500", "系统异常,请联系管理员!");
        }
    }
}

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

MarkerHub


Рекомендуются два оригинальных проекта SpringBoot + Vue, с полными пояснениями видео, документацией и исходным кодом:

[VueAdmin] Научит вас разрабатывать систему управления разделением интерфейсов и серверов SpringBoot+Jwt+Vue.

[VueBlog] Полное обучение проекту блога о разделении внешнего и внутреннего интерфейса на основе разработки SpringBoot + Vue.

Если у вас есть какие-либо вопросы, заходите в мой официальный аккаунт【Вопросы и ответы по Java】спросите меня