Spring Integration中HttpRequestExecutingMessageHandler ContentType覆盖问题求助
你的问题核心是HttpRequestExecutingMessageHandler没有正确设置请求的Content-Type为application/json,导致目标REST接口拒绝请求。我们先分析你代码里的问题,再给出几种可行的解决方案:
问题分析
你在配置DefaultHttpHeaderMapper时的逻辑有误:
HeaderMapper<HttpHeaders> mapper = new DefaultHttpHeaderMapper(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); mapper.toHeaders(httpHeaders);
这段代码只是把HttpHeaders转换为MessageHeaders,但并没有告诉HeaderMapper要将消息中的Content-Type头传递到HTTP请求里,也没有给HttpRequestExecutingMessageHandler设置默认的Content-Type。
解决方案
方法1:直接给HttpRequestExecutingMessageHandler设置默认Content-Type(最推荐)
如果你的目标接口固定只接受application/json,这是最简单的方式——直接调用handler的setContentType方法,无需处理消息头或HeaderMapper:
@Bean @ServiceActivator(inputChannel = "httpRequestOutChannel") public HttpRequestExecutingMessageHandler outbound() { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(documentConverterRestUrl); handler.setHttpMethod(HttpMethod.POST); handler.setErrorHandler(httpResponseErrorHandler); handler.setExpectedResponseType(String.class); handler.setCharset(Charset.defaultCharset().name()); // 直接设置请求的Content-Type handler.setContentType(MediaType.APPLICATION_JSON_VALUE); handler.setOutputChannel(httpResponseChannel()); return handler; }
这种方式会强制所有通过该handler发送的请求使用application/json作为Content-Type,无需修改消息转换的代码。
方法2:配置HeaderMapper传递消息中的Content-Type头
如果你需要根据不同消息动态设置Content-Type,可以在消息转换时添加Content-Type头,再配置HeaderMapper将这个头传递到HTTP请求:
首先修改transformRequest方法,给消息添加Content-Type头:
@ServiceActivator(inputChannel = "transformRequestToJsonChannel", outputChannel = "httpRequestOutChannel") public Message<?> transformRequest(Message<DocumentConverterRequest> message) { LOG.info("transforming document converter request to json: '{}'", message.getPayload()); ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); transformer.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); Object payload = transformer.transform(message).getPayload(); LOG.info("payload: '{}'", payload.toString()); // 给消息添加Content-Type头 return MessageBuilder.withPayload(payload) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) .build(); }
然后正确配置DefaultHttpHeaderMapper,让它传递这个头:
@Bean @ServiceActivator(inputChannel = "httpRequestOutChannel") public HttpRequestExecutingMessageHandler outbound() { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(documentConverterRestUrl); handler.setHttpMethod(HttpMethod.POST); handler.setErrorHandler(httpResponseErrorHandler); handler.setExpectedResponseType(String.class); handler.setCharset(Charset.defaultCharset().name()); // 配置HeaderMapper传递Content-Type头 DefaultHttpHeaderMapper headerMapper = (DefaultHttpHeaderMapper) DefaultHttpHeaderMapper.outboundMapper(); // 指定要传递的出站HTTP头(这里只需要Content-Type) headerMapper.setOutboundHeaderNames(new String[]{HttpHeaders.CONTENT_TYPE}); // 如果你需要传递所有标准请求头,可以用: // headerMapper.setOutboundHeaderNames(DefaultHttpHeaderMapper.STANDARD_REQUEST_HEADERS); handler.setHeaderMapper(headerMapper); handler.setOutputChannel(httpResponseChannel()); return handler; }
方法3:利用ObjectToJsonTransformer的自动头设置
其实ObjectToJsonTransformer在设置contentType后,会自动给转换后的消息添加对应的Content-Type头,你可以简化transformRequest方法:
@ServiceActivator(inputChannel = "transformRequestToJsonChannel", outputChannel = "httpRequestOutChannel") public Message<?> transformRequest(Message<DocumentConverterRequest> message) { LOG.info("transforming document converter request to json: '{}'", message.getPayload()); ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); transformer.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); // 直接返回转换后的消息,它已经带有Content-Type头 Message<?> transformedMessage = transformer.transform(message); LOG.info("payload: '{}'", transformedMessage.getPayload().toString()); return transformedMessage; }
然后再配合方法2中的HeaderMapper配置,就能正确传递Content-Type到HTTP请求。
总结
- 如果接口固定接受JSON,优先使用方法1,代码最简洁;
- 如果需要动态设置Content-Type,选择方法2+方法3的组合,让消息携带头信息并通过HeaderMapper传递。
内容的提问来源于stack exchange,提问作者Anton Styopin

