SpringBoot全局异常处理请求参数校验及响应体包装

一、响应体包装

  • 全局接口响应体包装,返回json数据
  • 支持对部分接口或者类放行
 # mvc配置mvc:body-exclude-paths:- /test/**body-exclude-classes:- com.qiangesoft.rdp.starter.XXX
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.List;/*** mvc接口包装配置** @author qiangesoft* @date 2023-09-18*/
@Data
@Configuration
@ConfigurationProperties(prefix = "mvc")
public class MvcProperties {/*** 忽略包装的接口*/private List<String> bodyExcludePaths = new ArrayList<>();/*** 忽略包装的类*/private List<Class> bodyExcludeClasses = new ArrayList<>();
}
import java.lang.annotation.*;/*** 返回结果包装忽略注解** @author qiangesoft* @date 2023-09-18*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@ResponseBody
public @interface ResponseBodyIgnore {
}
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qiangesoft.rdp.starter.mvc.config.MvcProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;/*** 返回结果包装** @author qiangesoft* @date 2023-09-18*/
@Slf4j
@RestControllerAdvice
public class RdpResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Autowiredprivate ObjectMapper objectMapper;@Autowiredprivate MvcProperties mvcProperties;/*** 接口忽略包装的注解*/private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseBodyIgnore.class;/*** 判断类或者方法是否使用了@ResponseBodyIgnore*/@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return !AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) && !returnType.hasMethodAnnotation(ANNOTATION_TYPE);}/*** 当类或者方法使用了 @ResponseResultBody 就会调用这个方法*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 配置接口忽略包装boolean ignorePath = this.isIgnore(request);if (ignorePath) {return body;}// 配置类忽略包装boolean ignoreClazz = this.isIgnore(returnType);if (ignoreClazz) {return body;}// 未拦截到的错误信息返回jsonClass<?> returnClass = returnType.getMethod().getReturnType();if (Objects.equals(returnClass, ResponseEntity.class) && body instanceof LinkedHashMap) {Map map = (LinkedHashMap) body;Integer status = (Integer) map.get("status");String error = (String) map.get("error");return ResponseInfo.fail(status, error);}// 如果返回类型是string,那么springmvc是直接返回的,此时需要手动转化为jsonif (body instanceof String) {return objectMapper.writeValueAsString(ResponseInfo.success(body));}// 防止重复包裹的问题出现if (body instanceof ResponseInfo) {return body;}return ResponseInfo.success(body);}/*** 是否忽略包装** @param request* @return*/private boolean isIgnore(ServerHttpRequest request) {String path = request.getURI().getPath();AntPathMatcher antPathMatcher = new AntPathMatcher();List<String> bodyExcludePaths = mvcProperties.getBodyExcludePaths();for (String excludePath : bodyExcludePaths) {boolean match = antPathMatcher.match(excludePath, path);if (match) {return true;}}return false;}/*** 是否忽略包装** @param returnType* @return*/private boolean isIgnore(MethodParameter returnType) {Class clazz = returnType.getContainingClass();List<Class> bodyExcludeClasses = mvcProperties.getBodyExcludeClasses();for (Class excludeClazz : bodyExcludeClasses) {if (excludeClazz.equals(clazz)) {return true;}}return false;}
}

二、全局异常处理

  • 全局异常拦截
  • 支持异常扩展
import com.qiangesoft.rdp.starter.mvc.exception.result.ExceptionResultHandler;
import com.qiangesoft.rdp.starter.mvc.exception.result.ResultMessageEnum;/*** 异常基类** @author qiangesoft* @date 2023-09-18*/
public class BaseException extends RuntimeException {private static final long serialVersionUID = 1L;/*** 错误码*/public int code;/*** 错误提示*/public String message;/*** 空构造方法,避免反序列化问题*/public BaseException() {}public BaseException(String message) {this.code = ResultMessageEnum.INTERNAL_SERVER_ERROR.getCode();this.message = message;}public BaseException(int code, String message) {this.code = code;this.message = message;}public BaseException(ExceptionResultHandler exceptionAdvice) {this.code = exceptionAdvice.getCode();this.message = exceptionAdvice.getMessage();}public int getCode() {return this.code;}public String getMessage() {return this.message;}}
/*** 异常信息接口** @author qiangesoft* @date 2023-09-18*/
public interface ExceptionResultHandler {/*** 编码** @return*/int getCode();/*** 详细信息** @return*/String getMessage();
}
/*** 业务异常枚举** @author qiangesoft* @date 2023-09-18*/
public enum ResultMessageEnum implements ExceptionResultHandler {/***********基础响应码***********/SUCCESS(200, "请求成功"),FAIL(500, "请求失败"),/***********常规响应码***********/PARAM_ERROR(4001, "请求参数错误"),PARAM_TYPE_ERROR(4002, "参数类型错误"),MESSAGE_NOT_READABLE(4003, "参数不可读"),BODY_MEDIA_TYPE_NOT_SUPPORT(4004, "请求体MediaType不支持"),UNAUTHORIZED(4011, "用户未登录"),FORBIDDEN(4031, "权限不足"),NOT_FOUND(4041, "请求资源不存在"),METHOD_NOT_ALLOWED(4051, "请求方式不正确"),INTERNAL_SERVER_ERROR(5001, "服务器内部错误,请稍后再试");private int code;private String message;ResultMessageEnum(int code, String message) {this.code = code;this.message = message;}@Overridepublic int getCode() {return this.code;}@Overridepublic String getMessage() {return this.message;}
}
import com.qiangesoft.rdp.starter.mvc.exception.BaseException;
import com.qiangesoft.rdp.starter.mvc.exception.NotLoginException;
import com.qiangesoft.rdp.starter.mvc.exception.result.ResultMessageEnum;
import com.qiangesoft.rdp.starter.mvc.response.ResponseInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.nio.file.AccessDeniedException;
import java.util.List;
import java.util.Set;/*** 全局异常处理*  * @author qiangesoft* @date 2023-09-18*/
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerAdvice {/*** 认证异常** @param exception* @return*/@ExceptionHandler(NotLoginException.class)@ResponseStatus(HttpStatus.UNAUTHORIZED)public ResponseInfo handleNotLoginException(NotLoginException exception) {return ResponseInfo.fail(ResultMessageEnum.UNAUTHORIZED);}/*** 权限异常** @param exception* @return*/@ExceptionHandler(AccessDeniedException.class)@ResponseStatus(HttpStatus.FORBIDDEN)public ResponseInfo handleAccessDeniedException(AccessDeniedException exception) {return ResponseInfo.fail(ResultMessageEnum.FORBIDDEN);}/*** 请求方式异常** @param exception* @return*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)public ResponseInfo handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException exception, HttpServletRequest request) {String method = request.getMethod();return ResponseInfo.fail(ResultMessageEnum.METHOD_NOT_ALLOWED.getCode(), String.format("请求方式%s不支持", method));}/*** 参数合法性校验异常* {@link org.springframework.validation.BindException}(以form-data形式传参)* {@link org.springframework.web.bind.MethodArgumentNotValidException}(常以body传参)** @param exception* @return*/@ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleBindException(BindException exception) {List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();StringBuilder message = new StringBuilder();for (FieldError fieldError : fieldErrors) {message.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";");}return ResponseInfo.fail(ResultMessageEnum.PARAM_ERROR.getCode(), message.toString());}/*** 参数合法性校验异常(通常是query或者form-data传参时的异常)** @param exception* @return*/@ExceptionHandler(ConstraintViolationException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleConstraintViolationException(ConstraintViolationException exception) {Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();StringBuilder message = new StringBuilder();for (ConstraintViolation<?> constraintViolation : constraintViolations) {message.append(constraintViolation.getPropertyPath()).append(constraintViolation.getMessage()).append(";");}return ResponseInfo.fail(ResultMessageEnum.PARAM_ERROR.getCode(), message.toString());}/*** ValidationException异常** @param exception* @return*/@ExceptionHandler(value = ValidationException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleValidationException(ValidationException exception) {return ResponseInfo.fail(ResultMessageEnum.PARAM_ERROR);}/*** 参数校验异常(以@RequestParam的传参的校验)* 例如:接口上设置了@RequestParam("xx")参数,结果并未传递xx参数** @param exception* @return*/@ExceptionHandler(MissingServletRequestParameterException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleMissingServletRequestParameterException(MissingServletRequestParameterException exception) {return ResponseInfo.fail(exception.getMessage());}/*** 参数类型异常(通常为表单传参错误导致参数无法类型转换)* 例如:接口上设置了@RequestParam("xx")参数为Integer,结果传递xx参数类型为String** @param exception* @return*/@ExceptionHandler(MethodArgumentTypeMismatchException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleMissingServletRequestParameterException(MethodArgumentTypeMismatchException exception) {return ResponseInfo.fail(ResultMessageEnum.PARAM_TYPE_ERROR.getCode(), exception.getName() + ResultMessageEnum.PARAM_TYPE_ERROR.getMessage());}/*** 参数不可读异常(通常为传参错误导致参数无法解析映射实体属性,body传参时参数类型错误也会进来)** @param exception* @return*/@ExceptionHandler(HttpMessageNotReadableException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public ResponseInfo handleHttpMessageNotReadableException(HttpMessageNotReadableException exception) {return ResponseInfo.fail(ResultMessageEnum.MESSAGE_NOT_READABLE.getCode(), exception.getMessage());}/*** MediaType不支持异常(通常为body传参时contentType错误)** @param exception* @return*/@ExceptionHandler(HttpMediaTypeNotSupportedException.class)@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)public ResponseInfo handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException exception) {return ResponseInfo.fail(ResultMessageEnum.BODY_MEDIA_TYPE_NOT_SUPPORT.getCode(), exception.getMessage());}/*** 【业务校验】过程中的非法参数异常* 该异常基本由{@link org.springframework.util.Assert}抛出** @param exception* @return*/@ExceptionHandler(IllegalArgumentException.class)@ResponseStatus(HttpStatus.OK)public ResponseInfo handleIllegalArgumentException(IllegalArgumentException exception) {return ResponseInfo.fail(ResultMessageEnum.INTERNAL_SERVER_ERROR.getCode(), exception.getMessage());}/*** 业务异常* 该异常基本由{@link com.qiangesoft.rdp.starter.mvc.exception.BaseException}及其子类抛出** @param exception* @return*/@ExceptionHandler(BaseException.class)@ResponseStatus(HttpStatus.OK)public ResponseInfo handleBaseException(BaseException exception) {return ResponseInfo.fail(exception.getCode(), exception.getMessage());}/*** 未知异常和错误(兜底处理)** @param throwable* @return*/@ExceptionHandler(Throwable.class)@ResponseStatus(HttpStatus.OK)public ResponseInfo handleThrowable(Throwable throwable) {return ResponseInfo.fail(ResultMessageEnum.INTERNAL_SERVER_ERROR);}
}

三、参数校验

1.使用hibernate-validator

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2.枚举值处理

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;/*** 枚举值校验注解** @author qiangesoft* @date 2023-09-18*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = EnumValueValidator.class)
public @interface EnumValue {String message() default "参数必须为指定的值!";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};Class<? extends IEnum> clazz();
}
import lombok.SneakyThrows;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;/*** 枚举值校验器** @author qiangesoft* @date 2023-09-18*/
public class EnumValueValidator implements ConstraintValidator<EnumValue, String> {private IEnum[] enums;@SneakyThrows@Overridepublic void initialize(EnumValue constraintAnnotation) {enums = constraintAnnotation.clazz().getEnumConstants();}@Overridepublic boolean isValid(String code, ConstraintValidatorContext context) {if (Objects.isNull(code)) {return false;}for (IEnum iEnum : enums) {if (iEnum.getCode().equals(code)) {return true;}}return false;}
}

3.业务枚举类需要实现此接口并重写getCode(),getDesc()

/*** 枚举定义** @author qiangesoft* @date 2023-09-18*/
public interface IEnum {/*** 获取枚举code*/String getCode();/*** 获取枚举描述*/String getDesc();
}

源码地址

https://gitee.com/qiangesoft/rdp-starter/tree/master/rdp-starter-mvc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/91854.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI绘画本地部署Stable Diffusion web UI

AI绘画本地部署Stable Diffusion web UI 一 、项目介绍 A browser interface based on Gradio library for Stable Diffusion. 项目地址&#xff1a;GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI 这是一个可以通过直接输入描述性文字文本来生成…

Verilog参数定义与仿真模块中的参数修改

文章目录 参数方式定义参数的优势rtl模块中的参数定义模块名后定义参数parameter定义参数 仿真模块中的参数修改例化时修改defparam修改 总结与说明附录&#xff1a;测试代码 参数方式定义参数的优势 当一个模块被另一个模块引用例化时&#xff0c;高层模块可以对低层模块的参…

MD5 绕过第三式:ffifdyop

文章目录 参考环境推荐阅读雾现两个 PHP 文件表结构分析 雾散ASCII 编码二进制数据到 ASCII 文本的转化绕过原理ffifdyop绕过 ffifdyop 的批量化生产批量化生产注意事项细节一字之差运算符优先级 实际需要遵守的规则 生产机器 参考 项目描述搜索引擎Bing、GoogleAI 大模型文心…

幂级数和幂级数的和函数有什么关系?

幂级数和幂级数的和函数有什么关系&#xff1f; 本文例子引用自&#xff1a;80_1幂级数运算&#xff0c;逐项积分、求导【小元老师】高等数学&#xff0c;考研数学 求幂级数 ∑ n 1 ∞ 1 n x n \sum\limits_{n1}^{\infty}\frac{1}{n}x^n n1∑∞​n1​xn 的和函数 &#xff…

分析各种表达式求值过程

目录 算术运算与赋值 编译器常用的两种优化方案 常量传播 常量折叠 加法 Debug编译选项组下编译后的汇编代码分析 Release开启02执行效率优先 减法 Release版下优化和加法一致&#xff0c;不再赘述 乘法 除法 算术结果溢出 自增和自减 关系运算与逻辑运算 JCC指…

乐鑫 ESP-Mesh-Lite在windows下的开发环境搭建

ESP-Mesh-Lite的开发环境由于没有官方教程&#xff0c;折腾了好几天。环境搭建主要还是参考ESP-MDF环境搭建&#xff0c;特别注意的是必须要在CMD环境下操作&#xff0c;不能用POWER SHELL。 ESP-Mesh-Lite目前支持到5.1的SDK&#xff0c;当然4.4也是可以用的。首先上Gitee或G…

Android 查看路由表

Android 查看路由表_android 路由表_念雅的博客-CSDN博客

百度统计配置详细图文教程包含siteId、百度统计AccessToken、百度统计代码获取步骤教程

一、前言 很多网友开发者都不知道百度统计siteId、百度统计token怎么获取&#xff0c;在网上找的教程都是几年前老的教程&#xff0c;因此给大家出一期详细百度统计siteId、百度统计token、百度统计代码获取详细步骤教程。 二、登录到百度统计 1.1 登录到百度统计官网 使用个…

不同部署环境下事务特性

RDBMS: 集中环境&#xff1a;ACID&#xff0c;强事务要求。采用JDBC事务 Atomicity(原子性),一个事务中所有操作都必须全部完成&#xff0c;要么全部不完成。 Consistency(一致性),在事务开始或结束时&#xff0c;数据库应该在一致状态。 Isolation(隔离性),事务将假定只有…

SPSS探索性分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件可在个人主页—…

StarRocks数据导入

1、相关环境 Flink作为当前流行的流式计算框架&#xff0c;在对接StarRocks时&#xff0c;若直接使用JDBC的方式"流式"写入数据&#xff0c;对StarRocks是不友好的&#xff0c;StarRocks作为一款MVCC的数据库&#xff0c;其导入的核心思想还是"攒微批降频率&qu…

Java Package用法:组织与管理类的利器

在Java开发中&#xff0c;package&#xff08;包&#xff09;是一种用于组织和管理类的机制。它提供了一种将相关类组织在一起的方式&#xff0c;使得类的命名更加清晰和易于管理。通过定义package&#xff0c;可以将类的命名空间进行划分&#xff0c;避免命名冲突&#xff0c;…

thinkphp csv格式导入导出

封装成函数&#xff1a; class Csv{/*** 导出csv文件*/public function export_csv($list, $title){$file_name "export" . time() . ".csv"; // 文件名header(Content-Type: application/vnd.ms-excel); // 设置内容类型为Excelheader(Content-Disposit…

SPSS列联表分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件可在个人主页—…

BUUCTF reverse wp 71 - 75

[NPUCTF2020]你好sao啊 int __cdecl main(int argc, const char **argv, const char **envp) {__int64 v3; // rax__int64 v4; // rdx__int64 v5; // raxsize_t v6; // rax__int64 v7; // rax__int64 v8; // rdx__int64 v9; // rax__int64 v11; // rdx__int64 v12; // raxchar …

【STM32】IAP升级01 bootloader实现以及APP配置(主要)

APP程序以及中断向量表的偏移设置 前言 通过之前的了解 之前的了解&#xff0c;我们知道实现IAP升级需要两个条件&#xff1a; 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始&#xff1b; 2.APP程序的中断向量表相应的移动&#xff0c;移动的偏移量为 x&#xff…

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…

cloudCompare教程:一、可视化、点、线编辑

依据高度等准则(都在Scalar Fields中)渲染点云&#xff08;首先要打开Tools -> Projection -> Export coordinate to SF&#xff09; 在上述准则之外的&#xff0c;设置为不显示&#xff1a; 软件的显示设置&#xff08;首先打开右边的彩色柱状图&#xff0c;点击左边属性…

TensorFlow 介绍 及其简单应用 附实例

TensorFlow是一种基于数据流编程的开源软件库&#xff0c;是人工智能领域中的重要工具&#xff0c;广泛应用于深度学习、自然语言处理等领域。 TensorFlow的基本概念包括&#xff1a; 张量&#xff08;Tensor&#xff09;&#xff1a;存储和传递数据的多维数组&#xff0c;包括…

ECharts多个数据视图进行自适应大小的解决方案

项目场景&#xff1a; 在制作数据视图时经常会遇到多个数据视图的情况&#xff0c;在多个数据视图的情况下做自适应是比较麻烦的&#xff0c;这里就详细的分析一下该如何去制作&#xff0c;分享一下我的解决办法及思路。 定义 DOM 容器 这里需要注意一个地方&#xff0c;在定…