SpringBoot中@ControllerAdvice的三种使用场景

一、全局异常处理

代码示例如下:

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 业务层异常枚举
 */
public enum ServiceExceptionEnum {
    SUCCESS(0, "成功"),
    ERROR(1, "失败"),
    SYS_ERROR(1000, "服务端发生异常"),
    MISSING_REQUEST_PARAM_ERROR(1001, "参数缺失"),
    INVALID_REQUEST_PARAM_ERROR(1002, "请求参数不合法");

    private final String message;

    private final int code;

    ServiceExceptionEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public int getCode() {
        return code;
    }
}
import com.example.quartzdemo.enums.ServiceExceptionEnum;

import java.io.Serializable;

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 统一返回结果实体类
 */
public class CommonResult implements Serializable {


    /**
     * 错误码
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 返回数据
     */
    private T data;

    /**
     * 成功
     *
     * @param data
     * @param 
     * @return
     */
    public static  CommonResult success(T data) {
        CommonResult commonResult = new CommonResult<>();
        commonResult.setCode(ServiceExceptionEnum.SUCCESS.getCode());
        commonResult.setMessage(ServiceExceptionEnum.SUCCESS.getMessage());
        commonResult.setData(data);
        return commonResult;
    }

    /**
     * 失败
     *
     * @param message
     * @param 
     * @return
     */
    public static  CommonResult error(String message) {
        CommonResult commonResult = new CommonResult<>();
        commonResult.setCode(ServiceExceptionEnum.ERROR.getCode());
        commonResult.setMessage(message);
        return commonResult;
    }

    /**
     * 失败
     *
     * @param message
     * @param 
     * @return
     */
    public static  CommonResult error(int code, String message) {
        CommonResult commonResult = new CommonResult<>();
        commonResult.setCode(code);
        commonResult.setMessage(message);
        return commonResult;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "CommonResult{" +
                "code=" + code +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

全局异常处理类:

import com.example.quartzdemo.common.CommonResult;
import com.example.quartzdemo.enums.ServiceExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 全局异常处理
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理 MissingServletRequestParameterException 异常
     * 

* SpringMVC 参数不正确 */ @ResponseBody @ExceptionHandler(value = MissingServletRequestParameterException.class) public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) { log.error("[missingServletRequestParameterExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage()); } @ResponseBody @ExceptionHandler(value = ConstraintViolationException.class) public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) { log.error("[constraintViolationExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(constraintViolation.getMessage()); } // 包装 CommonResult 结果 return CommonResult.error( ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } /** * 处理参数校验异常 * * @param req * @param ex * @return */ @ResponseBody @ExceptionHandler(value = BindException.class) public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) { log.info("========进入了 bindException======"); log.error("[bindExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ObjectError objectError : ex.getAllErrors()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(objectError.getDefaultMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } /** * 处理参数校验异常 * * @param req * @param ex * @return */ @ResponseBody @ExceptionHandler(value = MethodArgumentNotValidException.class) public CommonResult MethodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException ex) { log.info("-----------------进入了 MethodArgumentNotValidException-----------------"); log.error("[MethodArgumentNotValidException]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ObjectError objectError : ex.getBindingResult().getAllErrors()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(objectError.getDefaultMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } /** * 处理其它 Exception 异常 * * @param req * @param e * @return */ @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(HttpServletRequest req, Exception e) { // 记录异常日志 log.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(), ServiceExceptionEnum.SYS_ERROR.getMessage()); } }

二、全局数据绑定

全局数据绑定可以用来做一些初始化数据的操作,我们可以将一些公共的数据定义到添加了@ControllerAdvice注解的类中,这样我们就可以在每一个Controller中可以访问这些数据。

1.定义全局数据绑定类

import java.util.HashMap;
import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据绑定
 */
@ControllerAdvice
public class GlobalDataBindHandler {

    @ModelAttribute(name = "gd")
    public Map globalData() {
        Map map = new HashMap<>();
        map.put("website", "https://www.xx.com/");
        map.put("email", "[email protected]");
        return map;
    }
}

使用@ModelAttribute注解标记该方法返回的是一个全局数据。

2.我们在Controller类中使用这个全局数据

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据绑定测试
 */
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String toHello(Model model) {
        Map map = model.asMap();
        // 输出 {map={website=https://www.xx.com/, [email protected]}}
        System.out.println(map);
        return "hello";
    }
}

三、全局数据预处理

1.我们准备两个实体类

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: Author实体
 */
public class Author {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Author{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: Book实体
 */
public class Book {

    private String name;

    private Integer price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

我们定义一个访问接口

import com.example.quartzdemo.bean.Author;
import com.example.quartzdemo.bean.Book;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 测试
 */
@RestController
public class TestController {


    /**
     * 测试
     *
     * @param book   Book对象
     * @param author Author对象
     */
    @PostMapping("/book")
    public void addBook(Book book, Author author) {
        System.out.println(book);
        System.out.println(author);
    }
}

这个时候,我们的添加操作就会有问题,因为这两个实体类中都有一个name的属性,前端传递数据的时候,无法区分是哪个实体的name属性,这种情况我们可以通过@ControllerAdvice的全局数据预处理来解决这个问题。

我们在postman上进行测试

SpringBoot中@ControllerAdvice的三种使用场景_第1张图片

 控制台打印返回

Book{name='书名,作者', price=20}
Author{name='书名,作者', age=20}

发现数据混乱了,这不是我们需要的结果。

如何解决?

1.给接口中的变量取别名

import com.example.quartzdemo.bean.Author;
import com.example.quartzdemo.bean.Book;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qinxun
 * @date 2023-06-02
 * @Descripion: 测试
 */
@RestController
public class TestController {


    /**
     * 测试
     *
     * @param book   Book对象
     * @param author Author对象
     */
    @PostMapping("/book")
    public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
        System.out.println(book);
        System.out.println(author);
    }
}

2.全局数据处理类

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;

import java.util.HashMap;
import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据处理
 */
@ControllerAdvice
public class GlobalDataBindHandler {

    // 全局数据绑定
    @ModelAttribute
    public Map globalData() {
        Map map = new HashMap<>();
        map.put("website", "https://www.xx.com/");
        map.put("email", "[email protected]");
        return map;
    }

    // 对命名为b的全局数据预处理
    @InitBinder("b")
    public void b(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("b.");
    }

    // 对命名为a的全局数据预处理
    @InitBinder("a")
    public void a(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("a.");
    }
}

@InitBinder("b")表示该方法用来处理和Book相关的参数,给参数添加一个b前缀,要求参数必须有b前缀。

3.postman测试

SpringBoot中@ControllerAdvice的三种使用场景_第2张图片

 控制台打印返回

Book{name='书名', price=20}
Author{name='作者', age=20}

你可能感兴趣的:(SpringBoot,spring,boot,java,前端)