SpringBoot全局处理

x33g5p2x  于2021-12-06 转载在 Spring  
字(6.2k)|赞(0)|评价(0)|浏览(549)

一、错误页

SpringBoot默认已经存在了错误页的显示处理,例如访问一个不存在的路径:

如果考虑到前后端分离的设计,那么应该使用JSON进行数据交互,返回页面的形式就不能满足要求了。

新建返回数据包装类,规定任何交互数据都以此为规范:

public class ResultMessage<T> {
    private Integer code; // 返回编码
    private String message; // 信息
    private T result; // 返回结果
    private boolean success;

    public ResultMessage() {
    }
    
    public ResultMessage(Integer code, String message, T result, boolean success) {
        this.code = code;
        this.message = message;
        this.result = result;
        this.success = success;
    }
	// setter、getter略
}

新建ErrorAction专门返回错误信息:

@RestController
@RequestMapping("/errors")
public class ErrorAction {

    @RequestMapping(value = "/error_404")
    public ResultMessage<?> error404(HttpServletRequest request,HttpServletResponse response){
        response.setContentType("text/json");
        response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 设置响应编码
        Map<String, String> map = new LinkedHashMap<>();
        map.put("referer", request.getHeader("Referer")); // 之前的来源
        map.put("path", request.getRequestURI()); // 访问路径
        return new ResultMessage<>(404, "无法找到用户访问信息", map, false);
    }

}

新建配置类,使用/error/404代替真正的错误页显示:

@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar {
    /** * 注册错误页 * @param registry ErrorPageRegistry */
    public void registerErrorPages(ErrorPageRegistry registry) {
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/error_404");
        registry.addErrorPages(errorPage404);
    }
}

启动项目,访问一个不存在的路径:

其他类型的错误信息也可以采用同样的处理。

以500为例,在ErrorAction新增一个error500方法:

@RequestMapping(value = "/error_500")
 public ResultMessage<?> error500(HttpServletRequest request,HttpServletResponse response){
     Map<String, String> map = new LinkedHashMap<>();
     map.put("referer", request.getHeader("Referer")); // 之前的来源
     map.put("path", request.getRequestURI()); // 访问路径
     return new ResultMessage<>(500, "无法找到用户访问信息", map, false);
 }

修改ErrorPageConfig类的registerErrorPages方法,注册500错误页:

@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar {
    /** * 注册错误页 * @param registry ErrorPageRegistry */
    public void registerErrorPages(ErrorPageRegistry registry) {
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/error_404");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/error_500");
        registry.addErrorPages(errorPage404, errorPage500);
    }
}

新建TestController类,制造一个服务器错误:

@RestController
@RequestMapping("/fail/*")
public class TestController {

    @RequestMapping("/zero")
    public Integer zeroError(Integer x) {
        return 0 / x;
    }
}

启动项目,访问http://localhost:8080/fail/zero:

二、异常处理

SpringBoot中可以使用@ControllerAdviceRestControllerAdvice注解开启全局异常的捕获,使用@ExceptionHandler注解声明要捕获异常的类型。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResultMessage<?> exceptionHandler(HttpServletRequest request, Exception e) {
        Map<String, String> resultMap = new LinkedHashMap<>();
        resultMap.put("uri",request.getRequestURI());
        resultMap.put("type",e.getClass().getName());
        return new ResultMessage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), resultMap, false);
    }
}

创建一个产生异常的Action测试类:

@RestController
@RequestMapping("/fail/*")
public class TestController {

    @RequestMapping("/zero")
    public Integer zeroError(Integer x) {
        return 0 / x;
    }
}

启动项目,访问http://localhost:8080/fail/zero :

访问:http://localhost:8080/fail/zero?x=0

三、全局数据绑定

全局数据绑定用在一些返回相同数据的场景,修改了全局绑定的数据,那么所有的返回结果都会被更改。
新建Message类:

@Data
@AllArgsConstructor
public class Message {
    private Integer id;
    private String content;
    private Date pubDate;
}

新建MessageConfig类,将Message实例对象注入Spring容器:

@Configuration
public class MessageConfig {
    @Bean
    public Message getMessage() {
        return new Message(1,"SpringBoot全局数据绑定",new Date());
    }
}

新建GlobalDataBindAdvice配置全局数据:

@ControllerAdvice
public class GlobalDataBindAdvice { // 全局数据绑定
    private final Message message;

    @Autowired
    public GlobalDataBindAdvice(Message message) {
        this.message = message;
    }

    @ModelAttribute(value = "bindModel")
    public Object bindData() {
        Map<String, String> resultMap = new LinkedHashMap();
        resultMap.put("content", message.getContent());
        resultMap.put("pubDate", message.getPubDate().toString());
        return resultMap;
    }
}

新建GlobalDataAction测试数据绑定结果:

@RestController
public class GlobalDataAction {

    @RequestMapping("/message/echo")
    public ResultMessage<?> echo(Model model, String message) {
        Map<String, String> bindMap = (Map<String, String>) model.asMap().get("bindModel"); // 取得全局绑定数据
        bindMap.put("url", "/message/echo");
        return new ResultMessage(200, message, bindMap, false);
    }
}

启动项目,访问http://localhost:8080/message/echo?message=hello :

四、数据预处理

SpringMVC可以将接收的参数自动转换为VO对象,然而如果一次性接受两个对象且两个对象中含有相同名称的属性,那么转换就会出现问题。
新建Company类和Dept类,这两个类中含有相同名字的属性name

@Data
public class Company {
    private Integer cid;
    private String name;
}
@Data
public class Dept {
    private Integer deptId;
    private String name;
}

新建DeptAction同时接收这两个类的对象:

@RestController
@RequestMapping("/dept/*")
public class DeptAction {

    @RequestMapping("/get")
    public Object get(Company company, Dept dept) {
        Map<String,Object> resultMap = new LinkedHashMap<>();
        resultMap.put("company",company);
        resultMap.put("dept",dept);
        return resultMap;
    }
}

访问:http://localhost:8080/dept/get?cid=1&name=XX公司&deptId=22&name=财务部

发现重复的参数内容使用了,进行分割,并不是预期中的结果。实际上传递给Action的参数都会通过HttpServletRequest中的getParameterValues方法进行接收,接收到的内容全部都是数组,而Spring将数组的内容自动调用了toString()方法。
如果想要得到预期的结果,可以通过全局预处理来实现,新建GlobalDataPreAdvice设置参数前缀:

@ControllerAdvice
public class GlobalDataPreAdvice { // 全局数据预处理

    @InitBinder("company")
    public void company(WebDataBinder binder) { // 参数前缀
        binder.setFieldDefaultPrefix("company.");
    }

    @InitBinder("dept")
    public void dept(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("dept.");
    }
}

修改DeptAction指定参数前缀:

@RestController
@RequestMapping("/dept/*")
public class DeptAction {

    @RequestMapping("/get")
    public Object get(@ModelAttribute("company") Company company,
                      @ModelAttribute("dept") Dept dept) {
        Map<String, Object> resultMap = new LinkedHashMap<>();
        resultMap.put("company", company);
        resultMap.put("dept", dept);
        return resultMap;
    }
}

访问:http://localhost:8080/dept/get?company.cid=1&company.name=XX公司&dept.deptId=22&dept.name=财务部

相关文章