or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdinbound-endpoints.mdindex.mdjava-dsl.mdmanagement.mdmessage-conversion.mdmultipart-handling.mdoutbound-handlers.md
tile.json

message-conversion.mddocs/

Message Conversion and Header Mapping

HTTP message converters for content type handling and header mappers for bidirectional translation between Spring Integration MessageHeaders and HTTP headers. These components enable seamless conversion of HTTP requests/responses to Spring Integration messages and vice versa.

Key Information for Agents

Default Converters:

  • JSON: MappingJackson2HttpMessageConverter (if Jackson on classpath)
  • XML: MappingJackson2XmlHttpMessageConverter or JAXB converter (if available)
  • String: StringHttpMessageConverter
  • Form: FormHttpMessageConverter
  • Multipart: MultipartAwareFormHttpMessageConverter (must be explicitly configured)

Converter Selection:

  • Selected based on Content-Type header for reading
  • Selected based on Accept header for writing
  • First converter that canRead()/canWrite() is used
  • Order matters: converters tried in list order

Header Mapping:

  • Standard HTTP headers automatically mapped with http_ prefix
  • User-defined headers require explicit configuration
  • Header patterns support wildcards (X-*, Custom-*)
  • Special patterns: HTTP_REQUEST_HEADERS, HTTP_RESPONSE_HEADERS

Default Header Mapper:

  • DefaultHttpHeaderMapper provides intelligent mapping
  • Outbound: maps message headers to HTTP request headers
  • Inbound: maps HTTP response headers to message headers
  • User-defined header prefix configurable (default: empty)

Header Mapping Rules:

  • Standard headers (Content-Type, Accept, etc.) mapped automatically
  • Custom headers require pattern configuration
  • Headers can be excluded via exclusion patterns
  • Header values converted to appropriate types

Multipart Support:

  • MultipartAwareFormHttpMessageConverter required for file uploads
  • Must configure MultipartFileReader for file handling strategy
  • Supports application/x-www-form-urlencoded and multipart/form-data
  • Files available after request scope ends (unlike standard MultipartFile)

Serialization Support:

  • SerializingHttpMessageConverter for Java serialization
  • Supports application/x-java-serialized-object media type
  • Only Serializable classes supported
  • Use with caution (security and compatibility concerns)

Edge Cases:

  • If no converter matches, throws HttpMessageNotReadableException or HttpMessageNotWritableException
  • If Content-Type not specified, first converter that can handle type is used
  • If Accept header not specified, first converter that can write type is used
  • Empty request body results in null payload (for non-GET methods)
  • Empty response body results in null reply (when extractResponseBody=true)

Performance Considerations:

  • Converter selection happens per request (consider caching)
  • Large payloads may require streaming converters
  • Multipart files can be stored in memory or on disk (configurable)
  • Header mapping overhead is minimal but can be optimized with patterns

Capabilities

DefaultHttpHeaderMapper

Default HeaderMapper implementation for HTTP, mapping between Spring Integration MessageHeaders and HTTP HttpHeaders bidirectionally. Provides intelligent mapping with configurable patterns and prefixes.

public class DefaultHttpHeaderMapper
    implements HeaderMapper<HttpHeaders>, BeanFactoryAware, InitializingBean {

    public static final String CONTENT_MD5 = "Content-MD5";
    public static final String REFRESH = "Refresh";
    public static final String HTTP_REQUEST_HEADER_NAME_PATTERN = "HTTP_REQUEST_HEADERS";
    public static final String HTTP_RESPONSE_HEADER_NAME_PATTERN = "HTTP_RESPONSE_HEADERS";

    /**
     * Creates a default header mapper instance.
     */
    public DefaultHttpHeaderMapper();

    /**
     * Sets BeanFactory for dependency resolution.
     *
     * @param beanFactory the bean factory
     * @throws BeansException if error occurs
     */
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException;

    /**
     * Sets header names or patterns to map to HTTP request/response headers.
     * Supports wildcards (e.g., "X-Custom-*") and special patterns.
     *
     * @param outboundHeaderNames header name patterns
     */
    public void setOutboundHeaderNames(String... outboundHeaderNames);

    /**
     * Sets header names or patterns to map from HTTP request/response headers.
     * Supports wildcards (e.g., "X-Custom-*") and special patterns.
     *
     * @param inboundHeaderNames header name patterns
     */
    public void setInboundHeaderNames(String... inboundHeaderNames);

    /**
     * Sets excluded outbound standard request header names.
     * Headers matching these names will not be mapped to HTTP requests.
     *
     * @param excludedOutboundStandardRequestHeaderNames excluded header names
     */
    public void setExcludedOutboundStandardRequestHeaderNames(
        String... excludedOutboundStandardRequestHeaderNames);

    /**
     * Sets excluded inbound standard response header names.
     * Headers matching these names will not be mapped from HTTP responses.
     *
     * @param excludedInboundStandardResponseHeaderNames excluded header names
     */
    public void setExcludedInboundStandardResponseHeaderNames(
        String... excludedInboundStandardResponseHeaderNames);

    /**
     * Sets prefix for user-defined headers.
     * User headers without standard HTTP names will be prefixed.
     * Default: empty string (no prefix).
     *
     * @param userDefinedHeaderPrefix the prefix
     */
    public void setUserDefinedHeaderPrefix(String userDefinedHeaderPrefix);

    /**
     * Initializes ConversionService after properties set.
     *
     * @throws Exception if initialization fails
     */
    public void afterPropertiesSet() throws Exception;

    /**
     * Maps from Spring Integration MessageHeaders to HTTP HttpHeaders.
     * Converts message headers to HTTP request/response headers.
     *
     * @param headers source message headers
     * @param target target HTTP headers
     */
    public void fromHeaders(MessageHeaders headers, HttpHeaders target);

    /**
     * Maps from HTTP HttpHeaders to Spring Integration MessageHeaders.
     * Converts HTTP request/response headers to message headers.
     *
     * @param source source HTTP headers
     * @return map of message headers
     */
    public Map<String, Object> toHeaders(HttpHeaders source);

    /**
     * Creates default outbound mapper instance.
     * Pre-configured for mapping message headers to HTTP requests.
     *
     * @return outbound header mapper
     */
    public static DefaultHttpHeaderMapper outboundMapper();

    /**
     * Creates default inbound mapper instance.
     * Pre-configured for mapping HTTP responses to message headers.
     *
     * @return inbound header mapper
     */
    public static DefaultHttpHeaderMapper inboundMapper();
}

Usage Example - Basic Header Mapping:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;

@Configuration
public class HeaderMappingConfig {

    @Bean
    public DefaultHttpHeaderMapper httpHeaderMapper() {
        DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper();

        // Map specific headers to HTTP requests
        mapper.setOutboundHeaderNames(
            "Content-Type",
            "Accept",
            "Authorization",
            "X-Custom-*");  // Pattern for custom headers

        // Map specific headers from HTTP responses
        mapper.setInboundHeaderNames(
            "Content-Type",
            "X-Transaction-Id",
            "X-Status-*");

        return mapper;
    }

    @Bean
    public HttpRequestExecutingMessageHandler handlerWithMapper() {
        HttpRequestExecutingMessageHandler handler =
            new HttpRequestExecutingMessageHandler("https://api.example.com/data");

        handler.setHeaderMapper(httpHeaderMapper());
        handler.setHttpMethod(HttpMethod.POST);

        return handler;
    }
}

Usage Example - Outbound Mapper with Exclusions:

@Bean
public DefaultHttpHeaderMapper outboundHeaderMapper() {
    DefaultHttpHeaderMapper mapper = DefaultHttpHeaderMapper.outboundMapper();

    // Set user-defined header prefix
    mapper.setUserDefinedHeaderPrefix("X-App-");

    // Exclude specific standard headers
    mapper.setExcludedOutboundStandardRequestHeaderNames(
        "Cookie",
        "Set-Cookie");

    return mapper;
}

Usage Example - Inbound Mapper:

@Bean
public DefaultHttpHeaderMapper inboundHeaderMapper() {
    DefaultHttpHeaderMapper mapper = DefaultHttpHeaderMapper.inboundMapper();

    // Map all response headers
    mapper.setInboundHeaderNames("*");

    // Exclude specific headers from mapping
    mapper.setExcludedInboundStandardResponseHeaderNames(
        "Transfer-Encoding",
        "Connection");

    return mapper;
}

Usage Example - Custom Pattern Matching:

@Bean
public DefaultHttpHeaderMapper customPatternMapper() {
    DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper();

    // Map standard headers and custom patterns
    mapper.setOutboundHeaderNames(
        "HTTP_REQUEST_HEADERS",  // Standard request headers
        "Authorization",
        "X-*",                    // All headers starting with X-
        "Custom-*");              // All headers starting with Custom-

    mapper.setInboundHeaderNames(
        "HTTP_RESPONSE_HEADERS",  // Standard response headers
        "X-*",
        "Custom-*");

    return mapper;
}

HttpHeaders Constants

Abstract class containing constant definitions for HTTP-related message header names used in Spring Integration HTTP components.

public abstract class HttpHeaders {

    /**
     * Prefix for HTTP header names in Spring Integration messages.
     * Value: "http_"
     */
    public static final String PREFIX = "http_";

    /**
     * Header name for request URL.
     * Value: "http_requestUrl"
     */
    public static final String REQUEST_URL = "http_requestUrl";

    /**
     * Header name for HTTP request method.
     * Value: "http_requestMethod"
     */
    public static final String REQUEST_METHOD = "http_requestMethod";

    /**
     * Header name for user principal.
     * Value: "http_userPrincipal"
     */
    public static final String USER_PRINCIPAL = "http_userPrincipal";

    /**
     * Header name for HTTP status code.
     * Value: "http_statusCode"
     */
    public static final String STATUS_CODE = "http_statusCode";
}

Usage Example:

import org.springframework.integration.http.HttpHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

public class HttpHeaderExample {

    public Message<String> createMessageWithHttpHeaders(String payload) {
        return MessageBuilder
            .withPayload(payload)
            .setHeader(HttpHeaders.REQUEST_URL, "https://api.example.com/data")
            .setHeader(HttpHeaders.REQUEST_METHOD, "POST")
            .setHeader(HttpHeaders.STATUS_CODE, 200)
            .build();
    }

    public void processMessage(Message<?> message) {
        String requestUrl = (String) message.getHeaders().get(HttpHeaders.REQUEST_URL);
        String method = (String) message.getHeaders().get(HttpHeaders.REQUEST_METHOD);
        Integer statusCode = (Integer) message.getHeaders().get(HttpHeaders.STATUS_CODE);

        System.out.println("URL: " + requestUrl);
        System.out.println("Method: " + method);
        System.out.println("Status: " + statusCode);
    }
}

MultipartAwareFormHttpMessageConverter

HttpMessageConverter implementation that delegates to AllEncompassingFormHttpMessageConverter while adding capability to read multipart/form-data content in HTTP requests. Enables handling of file uploads and multipart form submissions.

public class MultipartAwareFormHttpMessageConverter
    implements HttpMessageConverter<MultiValueMap<String, ?>> {

    /**
     * Creates converter with default configuration.
     */
    public MultipartAwareFormHttpMessageConverter();

    /**
     * Sets the character set used for writing form data.
     * Default: UTF-8.
     *
     * @param charset the charset
     */
    public void setCharset(Charset charset);

    /**
     * Sets the MultipartFileReader to use when reading MultipartFile content.
     * Determines how uploaded files are processed.
     *
     * @param multipartFileReader the multipart file reader
     */
    public void setMultipartFileReader(
        MultipartFileReader<?> multipartFileReader);

    /**
     * Returns list of supported media types.
     * Includes application/x-www-form-urlencoded and multipart/form-data.
     *
     * @return list of supported media types
     */
    public List<MediaType> getSupportedMediaTypes();

    /**
     * Checks if this converter can read the given class and media type.
     *
     * @param clazz the class to check
     * @param mediaType the media type
     * @return true if can read
     */
    public boolean canRead(Class<?> clazz, MediaType mediaType);

    /**
     * Checks if this converter can write the given class and media type.
     *
     * @param clazz the class to check
     * @param mediaType the media type
     * @return true if can write
     */
    public boolean canWrite(Class<?> clazz, MediaType mediaType);

    /**
     * Reads multipart form data from HTTP input message.
     * Converts multipart request to MultiValueMap.
     *
     * @param clazz the target class
     * @param inputMessage the HTTP input message
     * @return the multipart form data as MultiValueMap
     * @throws IOException if read error occurs
     * @throws HttpMessageNotReadableException if conversion fails
     */
    public MultiValueMap<String, ?> read(
        Class<? extends MultiValueMap<String, ?>> clazz,
        HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException;

    /**
     * Writes form data to HTTP output message.
     * Converts MultiValueMap to HTTP form data.
     *
     * @param map the form data
     * @param contentType the content type
     * @param outputMessage the HTTP output message
     * @throws IOException if write error occurs
     * @throws HttpMessageNotWritableException if conversion fails
     */
    public void write(
        MultiValueMap<String, ?> map,
        MediaType contentType,
        HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException;
}

Usage Example - File Upload Endpoint:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.converter.MultipartAwareFormHttpMessageConverter;
import org.springframework.integration.http.multipart.FileCopyingMultipartFileReader;
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
import org.springframework.integration.http.inbound.RequestMapping;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;

@Configuration
public class MultipartConfig {

    @Bean
    public MultipartAwareFormHttpMessageConverter multipartConverter() {
        MultipartAwareFormHttpMessageConverter converter =
            new MultipartAwareFormHttpMessageConverter();

        // Configure multipart file reader
        FileCopyingMultipartFileReader reader =
            new FileCopyingMultipartFileReader();
        reader.setPrefix("upload_");
        reader.setSuffix(".tmp");

        converter.setMultipartFileReader(reader);
        converter.setCharset(StandardCharsets.UTF_8);

        return converter;
    }

    @Bean
    public HttpRequestHandlingMessagingGateway uploadGateway() {
        HttpRequestHandlingMessagingGateway gateway =
            new HttpRequestHandlingMessagingGateway();

        RequestMapping mapping = new RequestMapping();
        mapping.setPathPatterns("/upload");
        mapping.setMethods(HttpMethod.POST);
        mapping.setConsumes("multipart/form-data");
        gateway.setRequestMapping(mapping);

        // Set message converters
        List<HttpMessageConverter<?>> converters = new ArrayList<>();
        converters.add(multipartConverter());
        gateway.setMessageConverters(converters);

        gateway.setRequestChannel(uploadChannel());

        return gateway;
    }
}

Usage Example - Form Submission with Java DSL:

import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.http.dsl.Http;
import org.springframework.util.MultiValueMap;

@Bean
public IntegrationFlow formSubmissionFlow() {
    MultipartAwareFormHttpMessageConverter converter =
        new MultipartAwareFormHttpMessageConverter();

    return IntegrationFlow
        .from(Http.inboundGateway("/forms/submit")
            .requestMapping(m -> m
                .methods(HttpMethod.POST)
                .consumes("multipart/form-data"))
            .messageConverters(converter)
            .requestPayloadType(MultiValueMap.class))
        .handle((payload, headers) -> {
            MultiValueMap<String, ?> formData = (MultiValueMap<String, ?>) payload;
            // Process form data
            return processFormData(formData);
        })
        .get();
}

SerializingHttpMessageConverter

HttpMessageConverter implementation for Serializable instances using Java serialization. Supports application/x-java-serialized-object media type for binary Java object serialization over HTTP.

public class SerializingHttpMessageConverter
    extends AbstractHttpMessageConverter<Serializable> {

    /**
     * Creates new instance supporting application/x-java-serialized-object.
     */
    public SerializingHttpMessageConverter();

    /**
     * Checks if class is Serializable.
     * Only Serializable classes are supported.
     *
     * @param clazz the class to check
     * @return true if class is Serializable
     */
    protected boolean supports(Class<?> clazz);

    /**
     * Checks if can write Serializable to given media type.
     *
     * @param clazz the class
     * @param mediaType the media type
     * @return true if can write
     */
    public boolean canWrite(Class<?> clazz, MediaType mediaType);

    /**
     * Reads serialized object from HTTP input message.
     *
     * @param clazz the target class
     * @param inputMessage the HTTP input message
     * @return the deserialized object
     * @throws IOException if read error occurs
     * @throws HttpMessageNotReadableException if deserialization fails
     */
    protected Serializable readInternal(
        Class<? extends Serializable> clazz,
        HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException;

    /**
     * Writes serialized object to HTTP output message.
     *
     * @param object the object to serialize
     * @param outputMessage the HTTP output message
     * @throws IOException if write error occurs
     * @throws HttpMessageNotWritableException if serialization fails
     */
    protected void writeInternal(
        Serializable object,
        HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException;
}

Usage Example - Serialized Object Transfer:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.HttpMethod;

@Configuration
public class SerializationConfig {

    @Bean
    public SerializingHttpMessageConverter serializingConverter() {
        return new SerializingHttpMessageConverter();
    }

    @Bean
    public HttpRequestExecutingMessageHandler serializedObjectHandler() {
        HttpRequestExecutingMessageHandler handler =
            new HttpRequestExecutingMessageHandler(
                "https://api.example.com/serialized-data");

        handler.setHttpMethod(HttpMethod.POST);

        // Configure message converters
        List<HttpMessageConverter<?>> converters = new ArrayList<>();
        converters.add(serializingConverter());
        handler.setMessageConverters(converters);

        handler.setExpectedResponseType(SerializableResponse.class);

        return handler;
    }
}

// Serializable data class
class SerializableData implements Serializable {
    private static final long serialVersionUID = 1L;

    private String data;
    private int value;

    // Constructors, getters, setters
}

Usage Example - Java DSL with Serialization:

@Bean
public IntegrationFlow serializationFlow() {
    SerializingHttpMessageConverter converter =
        new SerializingHttpMessageConverter();

    return IntegrationFlow
        .from("serializableChannel")
        .handle(Http.outboundGateway("https://api.example.com/binary")
            .httpMethod(HttpMethod.POST)
            .messageConverters(converter)
            .expectedResponseType(SerializableResult.class))
        .get();
}

Advanced Configuration Patterns

Custom Header Mapping Strategy

Implement custom header mapping logic for specific requirements:

import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.MessageHeaders;

@Bean
public HttpRequestExecutingMessageHandler customHeaderMappingHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler("https://api.example.com/data");

    // Create custom header mapper
    HeaderMapper<HttpHeaders> customMapper = new HeaderMapper<HttpHeaders>() {
        @Override
        public void fromHeaders(MessageHeaders headers, HttpHeaders target) {
            // Map authorization
            if (headers.containsKey("auth_token")) {
                target.setBearerAuth((String) headers.get("auth_token"));
            }

            // Map custom headers with transformation
            headers.keySet().stream()
                .filter(key -> key.startsWith("custom_"))
                .forEach(key -> {
                    String httpHeader = key.replace("custom_", "X-Custom-");
                    target.add(httpHeader, headers.get(key).toString());
                });

            // Map content type
            if (headers.containsKey(MessageHeaders.CONTENT_TYPE)) {
                target.setContentType(
                    MediaType.parseMediaType(
                        headers.get(MessageHeaders.CONTENT_TYPE).toString()));
            }
        }

        @Override
        public Map<String, Object> toHeaders(HttpHeaders source) {
            Map<String, Object> headers = new HashMap<>();

            // Map response headers
            source.forEach((key, values) -> {
                if (!values.isEmpty()) {
                    headers.put("http_" + key.toLowerCase(), values.get(0));
                }
            });

            // Map status headers
            if (source.containsKey("X-Status")) {
                headers.put("response_status", source.getFirst("X-Status"));
            }

            return headers;
        }
    };

    handler.setHeaderMapper(customMapper);
    handler.setHttpMethod(HttpMethod.POST);

    return handler;
}

Conditional Header Mapping

Map headers conditionally based on message content:

@Bean
public DefaultHttpHeaderMapper conditionalHeaderMapper() {
    DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper();

    // Configure base patterns
    mapper.setOutboundHeaderNames(
        "Content-Type",
        "Accept");

    // Custom mapping logic via BeanPostProcessor
    return new DefaultHttpHeaderMapper() {
        @Override
        public void fromHeaders(MessageHeaders headers, HttpHeaders target) {
            super.fromHeaders(headers, target);

            // Conditional mapping
            if (headers.containsKey("secure") &&
                Boolean.TRUE.equals(headers.get("secure"))) {
                // Add security headers for secure messages
                target.add("X-Security-Level", "HIGH");
                if (headers.containsKey("api_key")) {
                    target.add("X-API-Key", headers.get("api_key").toString());
                }
            }

            // Dynamic header based on payload type
            if (headers.containsKey(MessageHeaders.CONTENT_TYPE)) {
                String contentType = headers.get(MessageHeaders.CONTENT_TYPE).toString();
                if (contentType.contains("json")) {
                    target.add("X-Data-Format", "JSON");
                } else if (contentType.contains("xml")) {
                    target.add("X-Data-Format", "XML");
                }
            }
        }
    };
}

Multiple Converter Configuration

Configure multiple message converters for different content types:

@Configuration
public class MultiConverterConfig {

    @Bean
    public HttpRequestHandlingMessagingGateway multiConverterGateway() {
        HttpRequestHandlingMessagingGateway gateway =
            new HttpRequestHandlingMessagingGateway();

        RequestMapping mapping = new RequestMapping();
        mapping.setPathPatterns("/api/data");
        mapping.setMethods(HttpMethod.POST);
        mapping.setConsumes("application/json", "application/xml",
                            "application/x-www-form-urlencoded",
                            "multipart/form-data");
        gateway.setRequestMapping(mapping);

        // Configure multiple converters
        List<HttpMessageConverter<?>> converters = new ArrayList<>();

        // JSON converter
        MappingJackson2HttpMessageConverter jsonConverter =
            new MappingJackson2HttpMessageConverter();
        jsonConverter.setSupportedMediaTypes(
            Collections.singletonList(MediaType.APPLICATION_JSON));
        converters.add(jsonConverter);

        // XML converter
        MappingJackson2XmlHttpMessageConverter xmlConverter =
            new MappingJackson2XmlHttpMessageConverter();
        xmlConverter.setSupportedMediaTypes(
            Collections.singletonList(MediaType.APPLICATION_XML));
        converters.add(xmlConverter);

        // Form converter
        FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
        converters.add(formConverter);

        // Multipart converter
        MultipartAwareFormHttpMessageConverter multipartConverter =
            new MultipartAwareFormHttpMessageConverter();
        multipartConverter.setMultipartFileReader(
            new FileCopyingMultipartFileReader());
        converters.add(multipartConverter);

        // String converter (fallback)
        StringHttpMessageConverter stringConverter =
            new StringHttpMessageConverter(StandardCharsets.UTF_8);
        converters.add(stringConverter);

        gateway.setMessageConverters(converters);
        gateway.setRequestChannel(dataChannel());

        return gateway;
    }
}

Content Negotiation with Converters

Configure content negotiation for outbound requests:

@Bean
public HttpRequestExecutingMessageHandler contentNegotiationHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler("https://api.example.com/data");

    // Configure multiple converters for content negotiation
    List<HttpMessageConverter<?>> converters = new ArrayList<>();

    // JSON - preferred
    MappingJackson2HttpMessageConverter jsonConverter =
        new MappingJackson2HttpMessageConverter();
    converters.add(jsonConverter);

    // XML - alternate
    MappingJackson2XmlHttpMessageConverter xmlConverter =
        new MappingJackson2XmlHttpMessageConverter();
    converters.add(xmlConverter);

    handler.setMessageConverters(converters);
    handler.setHttpMethod(HttpMethod.POST);

    // Configure header mapper to set Accept header
    DefaultHttpHeaderMapper headerMapper = new DefaultHttpHeaderMapper();
    headerMapper.setOutboundHeaderNames("Content-Type", "Accept");
    handler.setHeaderMapper(headerMapper);

    return handler;
}

Header Mapping with Java DSL

Configure header mapping using Java DSL:

@Bean
public IntegrationFlow headerMappingFlow() {
    DefaultHttpHeaderMapper headerMapper = new DefaultHttpHeaderMapper();
    headerMapper.setOutboundHeaderNames(
        "Content-Type",
        "Authorization",
        "X-Request-*");
    headerMapper.setInboundHeaderNames(
        "Content-Type",
        "X-Response-*");

    return IntegrationFlow
        .from("inputChannel")
        .handle(Http.outboundGateway("https://api.example.com/process")
            .httpMethod(HttpMethod.POST)
            .headerMapper(headerMapper)
            .mappedRequestHeaders("Authorization", "X-Request-Id")
            .mappedResponseHeaders("X-Transaction-Id", "X-Status")
            .expectedResponseType(String.class))
        .get();
}

Common Use Cases

REST API with JSON

Configure JSON message conversion for REST API:

@Configuration
public class JsonApiConfig {

    @Bean
    public IntegrationFlow jsonApiFlow() {
        MappingJackson2HttpMessageConverter jsonConverter =
            new MappingJackson2HttpMessageConverter();

        DefaultHttpHeaderMapper headerMapper =
            DefaultHttpHeaderMapper.outboundMapper();
        headerMapper.setOutboundHeaderNames(
            "Content-Type",
            "Accept",
            "Authorization");

        return IntegrationFlow
            .from(Http.inboundGateway("/api/users")
                .requestMapping(m -> m
                    .methods(HttpMethod.POST)
                    .consumes("application/json")
                    .produces("application/json"))
                .messageConverters(jsonConverter)
                .headerMapper(headerMapper)
                .requestPayloadType(User.class))
            .handle("userService", "createUser")
            .get();
    }
}

File Upload with Metadata

Handle file uploads with additional metadata:

@Bean
public IntegrationFlow fileUploadWithMetadataFlow() {
    MultipartAwareFormHttpMessageConverter converter =
        new MultipartAwareFormHttpMessageConverter();
    converter.setMultipartFileReader(new DefaultMultipartFileReader());

    return IntegrationFlow
        .from(Http.inboundGateway("/api/files/upload")
            .requestMapping(m -> m
                .methods(HttpMethod.POST)
                .consumes("multipart/form-data"))
            .messageConverters(converter)
            .requestPayloadType(MultiValueMap.class))
        .<MultiValueMap<String, Object>>handle((payload, headers) -> {
            // Extract file
            MultipartFile file = (MultipartFile) payload.getFirst("file");

            // Extract metadata
            String description = (String) payload.getFirst("description");
            String category = (String) payload.getFirst("category");

            // Process upload
            return fileService.processUpload(file, description, category);
        })
        .get();
}

Custom Header Propagation

Propagate custom headers through integration flow:

@Bean
public IntegrationFlow headerPropagationFlow() {
    DefaultHttpHeaderMapper inboundMapper =
        DefaultHttpHeaderMapper.inboundMapper();
    inboundMapper.setInboundHeaderNames("X-*", "Authorization");

    DefaultHttpHeaderMapper outboundMapper =
        DefaultHttpHeaderMapper.outboundMapper();
    outboundMapper.setOutboundHeaderNames("X-*", "Authorization");

    return IntegrationFlow
        .from(Http.inboundGateway("/api/proxy")
            .headerMapper(inboundMapper))
        .handle(Http.outboundGateway("https://backend.example.com/api")
            .headerMapper(outboundMapper))
        .get();
}