如何从@Controller向@ControllerAdvice传递变量?并在自定义@ControllerAdvice验证处理器中添加额外提示信息
解决方案
一、添加「发票处理失败」的自定义验证提示
要实现这个需求,你可以通过自定义业务异常类结合@ControllerAdvice的全局捕获来完成,步骤清晰且易维护:
- 定义自定义异常类
先创建一个专门针对发票处理的异常类,用来携带错误提示和额外信息:
public class InvoiceProcessingException extends RuntimeException { // 可添加额外字段存储需要传递的变量 private String invoiceNo; private String errorDetail; // 基础构造方法,传入核心提示信息 public InvoiceProcessingException(String message) { super(message); } // 重载构造方法,支持携带更多业务变量 public InvoiceProcessingException(String message, String invoiceNo, String errorDetail) { super(message); this.invoiceNo = invoiceNo; this.errorDetail = errorDetail; } // getter方法,供全局异常处理器获取变量 public String getInvoiceNo() { return invoiceNo; } public String getErrorDetail() { return errorDetail; } }
- 在验证逻辑中抛出异常
在你的业务验证代码里,当发票不符合处理要求时,直接抛出这个自定义异常,带上「发票处理失败」的提示:
@Service public class InvoiceService { public void processInvoice(InvoiceDTO invoice) { // 自定义验证逻辑:比如检查发票号格式、金额是否合法等 if (isInvalidInvoice(invoice)) { // 抛出异常,携带提示信息和关键业务变量 throw new InvoiceProcessingException("发票处理失败:格式不符合要求", invoice.getInvoiceNo(), "缺少必填字段:发票金额"); } // 正常处理逻辑... } private boolean isInvalidInvoice(InvoiceDTO invoice) { // 你的验证逻辑实现 return invoice.getInvoiceNo() == null || invoice.getAmount() == null; } }
- 在
@ControllerAdvice中捕获并返回响应
在全局异常处理器中,针对InvoiceProcessingException编写专属处理逻辑,将提示信息和变量返回给前端:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(InvoiceProcessingException.class) @ResponseBody public ResponseEntity<ErrorResponse> handleInvoiceException(InvoiceProcessingException e) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("INVOICE_PROCESS_ERROR"); errorResponse.setMessage(e.getMessage()); // 这里会包含「发票处理失败」的提示 // 把异常中的额外变量也返回给前端 errorResponse.setExtra(Map.of("invoiceNo", e.getInvoiceNo(), "detail", e.getErrorDetail())); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } // 其他异常处理方法... } // 定义统一的错误响应体 class ErrorResponse { private String code; private String message; private Map<String, Object> extra; // getter和setter方法 }
二、从@Controller向@ControllerAdvice传递变量
传递变量的核心是让变量和异常或请求上下文绑定,下面两种方案是实际开发中最常用的:
方案1:通过自定义异常类携带变量(推荐)
这是最符合面向对象设计的方式——把需要传递的变量直接封装在自定义异常类中,抛出异常时赋值,在@ControllerAdvice中通过异常对象获取。这种方式逻辑清晰,变量和异常强关联,不会出现上下文污染。
就是上面示例中展示的方式:在InvoiceProcessingException中添加invoiceNo、errorDetail等字段,抛出时传入值,全局异常处理器中通过e.getInvoiceNo()获取。
方案2:通过RequestContextHolder存储请求上下文变量
如果不想修改现有异常类,也可以利用Spring的RequestContextHolder在Controller中存储变量,再在@ControllerAdvice中取出:
- 在Controller中存储变量:
@Controller public class InvoiceController { @PostMapping("/invoice/process") public ResponseEntity<Void> processInvoice(@RequestBody InvoiceDTO invoice) { try { // 将变量存入请求上下文 RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); if (attributes != null) { attributes.setAttribute("currentInvoiceNo", invoice.getInvoiceNo(), RequestAttributes.SCOPE_REQUEST); } invoiceService.processInvoice(invoice); return ResponseEntity.ok().build(); } catch (Exception e) { throw e; // 交给全局异常处理器处理 } } }
- 在
@ControllerAdvice中取出变量:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public ResponseEntity<ErrorResponse> handleGenericException(Exception e) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("UNKNOWN_ERROR"); errorResponse.setMessage(e.getMessage()); // 从请求上下文取出变量 RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); if (attributes != null) { String invoiceNo = (String) attributes.getAttribute("currentInvoiceNo", RequestAttributes.SCOPE_REQUEST); if (invoiceNo != null) { errorResponse.setExtra(Map.of("invoiceNo", invoiceNo)); } } return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } }
注意事项
- 优先选择方案1,它的代码可读性和维护性更好,避免了上下文变量的隐式传递。
- 如果使用方案2,要注意在异常抛出前完成变量存储,Spring的
RequestContextHolder已经做了线程隔离处理,无需担心多线程环境下的安全问题。
内容的提问来源于stack exchange,提问作者Adam Abdullah
相关产品推荐
相关产品推荐

