如何在Apache Camel验证请求Header时返回自定义响应
问题描述
我是Apache Camel新手,正在基于Quarkus开发微服务,目前要实现输入字段的自动化验证,需对请求Header(x-correlator)进行验证,并返回400状态码和自定义响应格式。我已在DSL中配置了clientRequestValidation(true)和验证参数的代码:
param().name("x-correlator").type(RestParamType.header).required(true).endParam()
但当前返回的默认响应不符合预期,我希望得到自定义格式的响应。以下是我的RestRoute代码:
@ApplicationScoped public class RestRoute extends RouteBuilder { @ConfigProperty(name = "client.url.userProfile") String urlUserProfile; @ConfigProperty(name = "client.utl.getActiveSessionByIp") String urlGetActivateSessionByIp; @ConfigProperty(name = "path.openapi") String pathOpenapi; @ConfigProperty(name = "descripcion.servicio") String descriptionService; private final IGetCurrentDateTime getCurrentDateTime; private ConfigureSsl configureSsl; private static final String SALIDA_BSS_EXCEPTION = "Salida Microservicio IdentifyOrigin ${body}"; private static final String MSG_EXCEPTION = "Descripcion de la Exception: ${exception.message}"; private static final String DATE_LOG = "[${bean:BeanDate.getCurrentDateTime()}] "; public RestRoute() { getCurrentDateTime = new GetCurrentDateTime(); TimeZone.setDefault(TimeZone.getTimeZone("GMT-4")); configureSsl= new ConfigureSsl(); } @Override public void configure() throws Exception { BeanDate beanDate= new BeanDate(); getContext().getRegistry().bind("BeanDate", beanDate); restConfiguration().bindingMode(RestBindingMode.json).dataFormatProperty("json.in.disableFeatures","FAIL_ON_UNKNOWN_PROPERTIES") .clientRequestValidation(true) .apiContextPath(pathOpenapi) .apiProperty("api.title","IdentifyOrigin") .apiProperty("api.description",descriptionService) .apiProperty("api-version","1.0.0") .apiProperty("cors","true"); rest("/api/") .produces("application/json") .consumes("application/json") .post("/identifyOrigin/v1/info") .type(Request.class) .param().name("x-correlator").type(RestParamType.header).required(true).endParam() .outType(ResponseSuccess.class) .param().name("Response").type(RestParamType.body).description("Parametros de Salidas") .required(true) .endParam().to("direct:pipeline"); from("direct:pipeline") .doTry() .to("bean-validator:validateRequest") .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "Datos de Entrada del MS: ${body}") .process(new GetActivateSessionByIpReqProcessor()) .log("\n[${bean:BeanDate.getCurrentDateTime()}] "+"Entrada Microservicio GetActivateSessionByIp: ${exchangeProperty[getActivateSessionByIpRequest]}") .to(configureSsl.setupSSLContext(getCamelContext(), urlGetActivateSessionByIp)) .choice() .when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(200)) .process(new GetActivateSessionByIpResProcessor()) .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "Salida MS GetActivateSessionByIp: ${body}") .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "PhoneNumberByIp obtenido desde MS GetActivateSessionByIp: ${exchangeProperty[phoneNumberByIp]}") .process(new UserProfileReqProcessor()) .to(configureSsl.setupSSLContext(getCamelContext(), urlUserProfile)) .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "Datos de Salida del MS UserProfile: ${body}") .otherwise() .process(new ApiResponseFaultProcessor()) .end() .process(new UserProfileReqProcessor()) .to(configureSsl.setupSSLContext(getCamelContext(), urlUserProfile)) .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "Datos de Salida del MS UserProfile: ${body}") .choice() .when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(200)) .process(new UserProfileResProcessor()) .log("["+"${bean:BeanDate.getCurrentDateTime()}"+"] "+ "UserId obtenido desde UserProfile: ${exchangeProperty[userId]}") .process(new IdentifyOriginResProcessor()) .log(DATE_LOG+"Salida MS IdentifyOrigin: ${exchangeProperty[identifyOriginResponse]}") .otherwise() .process(new ApiResponseFaultProcessor()) .end() .endDoTry() .doCatch(BeanValidationException.class) .process(new InvalidFormatExcepctionProcessor()) .log(DATE_LOG+MSG_EXCEPTION) .log(DATE_LOG+SALIDA_BSS_EXCEPTION) .doCatch(NotFoundDataException.class) .process(new NotFoundDataExceptionProcessor()) .log(DATE_LOG+MSG_EXCEPTION) .log(DATE_LOG+SALIDA_BSS_EXCEPTION) .doCatch(HttpHostConnectException.class) .process(new HttpHostConnectExceptionProcessor()) .log(DATE_LOG+MSG_EXCEPTION) .log(DATE_LOG+SALIDA_BSS_EXCEPTION) .doCatch(Exception.class) .process(new IdentifyOriginExceptionProcessor()) .log(DATE_LOG+MSG_EXCEPTION) .log(DATE_LOG+SALIDA_BSS_EXCEPTION); } }
请问如何实现自定义响应?
解决方案
核心问题是clientRequestValidation(true)触发的异常是RestValidationException,而非你当前捕获的BeanValidationException,导致默认响应未被自定义逻辑处理。按以下步骤修改:
1. 添加RestValidationException捕获逻辑
在路由的doCatch块中新增对该异常的捕获,指定自定义处理器:
.doCatch(RestValidationException.class) .process(new RestValidationExceptionProcessor()) .log(DATE_LOG+MSG_EXCEPTION) .log(DATE_LOG+SALIDA_BSS_EXCEPTION)
注意将这个doCatch放在Exception类型捕获的前面,因为Camel会按顺序匹配异常,更具体的异常需要优先处理。
2. 实现自定义异常处理器
创建RestValidationExceptionProcessor类,将异常信息转换为你需要的响应格式,并设置400状态码:
public class RestValidationExceptionProcessor implements Processor { @Override public void process(Exchange exchange) throws Exception { RestValidationException exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, RestValidationException.class); // 构建自定义错误响应实体 ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("400"); errorResponse.setMessage("请求参数验证失败"); errorResponse.setDetails(exception.getErrors().stream() .map(RestValidationError::getMessage) .collect(Collectors.toList())); // 设置响应体和状态码 exchange.getIn().setBody(errorResponse); exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400); } }
(注:ErrorResponse是你自定义的响应实体类,需根据业务需求调整字段结构)
3. 可选:全局异常处理替代路由内捕获
如果需要统一处理所有RestValidationException,可以使用Camel的全局异常处理机制,替代路由内的doCatch:
@Override public void configure() throws Exception { // 全局异常处理 onException(RestValidationException.class) .handled(true) .process(new RestValidationExceptionProcessor()) .log(DATE_LOG+MSG_EXCEPTION); // 原有路由配置... }
补充说明
clientRequestValidation负责验证Rest DSL中定义的参数(如header、path参数),而bean-validator负责验证请求体的Bean校验注解,两者触发的异常类型不同,需分别处理。- 确保
ErrorResponse类配置了正确的JSON序列化注解(如Jackson的@JsonProperty),保证响应格式符合预期。
内容的提问来源于stack exchange,提问作者Cesar Justo
相关产品推荐
相关产品推荐

