Spring Boot:返回非指定响应类型时抛出自定义异常
Spring Boot API强制返回指定响应类型的实现
需求说明
所有API接口必须返回ResponseEntity<MyResponseType>类型的响应,若接口返回其他类型,则抛出自定义异常InvalidResponseTypeException,向用户返回统一格式的错误信息。
核心代码定义
1. 统一响应实体类
public class MyResponseType { private String message; private int statusCode; // 带参构造方法 public MyResponseType(String message, int statusCode) { this.message = message; this.statusCode = statusCode; } // Getter & Setter public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } }
2. 自定义异常类
public class InvalidResponseTypeException extends RuntimeException { public InvalidResponseTypeException(String message) { super(message); } }
3. 控制器示例
@RestController @RequestMapping("/api") public class MyController { // 合法接口:返回指定类型响应 @GetMapping("/example-success") public ResponseEntity<MyResponseType> getExampleSuccess() { MyResponseType response = new MyResponseType("这是一个合法响应", 200); return ResponseEntity.ok(response); } // 非法接口:返回String类型响应,调用时会触发异常 @GetMapping("/example-failure") public ResponseEntity<String> getExampleFailure() { String response = "这是一个非法响应"; return ResponseEntity.ok(response); } }
实现自动校验的两种方案
方案一:使用ResponseBodyAdvice全局拦截
通过ResponseBodyAdvice可以在响应写出前校验返回类型,拦截所有@RestController的接口:
@ControllerAdvice public class ResponseTypeValidator implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 仅处理标注了@RestController的类中的方法 return returnType.getDeclaringClass().isAnnotationPresent(RestController.class); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { validateReturnType(returnType.getGenericParameterType()); return body; } private void validateReturnType(Type returnType) { // 校验返回类型是否为ResponseEntity<MyResponseType> if (!(returnType instanceof ParameterizedType)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } ParameterizedType paramType = (ParameterizedType) returnType; if (!paramType.getRawType().equals(ResponseEntity.class)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } Type[] actualTypes = paramType.getActualTypeArguments(); if (actualTypes.length != 1 || !actualTypes[0].equals(MyResponseType.class)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } } }
方案二:使用AOP切面拦截
通过AOP切面拦截控制器方法的执行,在方法调用前校验返回类型:
@Aspect @Component public class ResponseTypeValidationAspect { // 拦截控制器包下的所有公共方法,替换为实际包路径 @Around("execution(public * com.your.package.controller.*.*(..))") public Object validateResponseType(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); validateReturnType(signature.getMethod().getGenericReturnType()); return joinPoint.proceed(); } private void validateReturnType(Type returnType) { if (!(returnType instanceof ParameterizedType)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } ParameterizedType paramType = (ParameterizedType) returnType; if (!paramType.getRawType().equals(ResponseEntity.class)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } Type[] actualTypes = paramType.getActualTypeArguments(); if (actualTypes.length != 1 || !actualTypes[0].equals(MyResponseType.class)) { throw new InvalidResponseTypeException("API返回类型必须为ResponseEntity<MyResponseType>"); } } }
全局异常处理
添加全局异常处理器,将自定义异常转换为统一格式的响应:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(InvalidResponseTypeException.class) public ResponseEntity<MyResponseType> handleInvalidResponseType(InvalidResponseTypeException ex) { MyResponseType errorResp = new MyResponseType(ex.getMessage(), 400); return ResponseEntity.badRequest().body(errorResp); } }
内容的提问来源于stack exchange,提问作者Derda Karaaslan
相关产品推荐
相关产品推荐

