HTTP 支持

Spring Integration 的 HTTP 支持允许 HTTP 请求的运行和入站 HTTP 请求的处理。HTTP 支持包括以下网关实现:HttpInboundEndpointHttpRequestExecutingMessageHandler. 另请参阅WebFlux 支持

您需要将此依赖项包含到您的项目中:

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-http</artifactId>
    <version>5.5.13</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-http:5.5.13"

javax.servlet:javax.servlet-api必须在目标 Servlet 容器上提供依赖项。

Http 入站组件

要通过 HTTP 接收消息,您需要使用 HTTP 入站通道适配器或 HTTP 入站网关。为了支持 HTTP 入站适配器,它们需要部署在 Servlet 容器中,例如Apache TomcatJetty。最简单的方法是使用 Spring HttpRequestHandlerServlet,通过在文件中提供以下 servlet 定义web.xml

<servlet>
    <servlet-name>inboundGateway</servlet-name>
    <servlet-class>o.s.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>

请注意,servlet 名称与 bean 名称匹配。有关使用 的更多信息HttpRequestHandlerServlet,请参阅使用 Spring 的远程处理和 Web 服务,它是 Spring 框架参考文档的一部分。

如果您在 Spring MVC 应用程序中运行,则不需要前面提到的显式 servlet 定义。在这种情况下,网关的 bean 名称可以与 URL 路径进行匹配,就像 Spring MVC 控制器 bean 一样。有关更多信息,请参阅 Web MVC 框架,它是 Spring 框架参考文档的一部分。

有关示例应用程序和相应的配置,请参阅Spring Integration Samples存储库。它包含HTTP 示例应用程序,它演示了 Spring Integration 的 HTTP 支持。

以下示例 bean 定义了一个 HTTP 入站端点:

<bean id="httpInbound"
  class="org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway">
  <property name="requestChannel" ref="httpRequestChannel" />
  <property name="replyChannel" ref="httpReplyChannel" />
</bean>

HttpRequestHandlingMessagingGateway接受实例列表或HttpMessageConverter依赖于默认列表。转换器允许自定义从HttpServletRequest到的映射Message。默认转换器封装了简单的策略,(例如)为内容类型以 . 开头String的请求创建消息。有关完整的详细信息,请参阅Javadoc。可以与自定义列表一起设置附加标志 ( ),以在自定义转换器之后添加默认转换器。默认情况下,此标志设置为,这意味着自定义转换器替换默认列表。POSTtextmergeWithDefaultConvertersHttpMessageConverterfalse

消息转换过程使用(可选)requestPayloadType属性和传入Content-Type标头。从 4.3 版开始,如果请求没有内容类型标头,application/octet-stream则假定为RFC 2616. 以前,此类消息的正文被忽略。

Spring Integration 2.0 实现了多部分文件支持。如果请求已包装为MultipartHttpServletRequest,则当您使用默认转换器时,该请求将转换为包含值的Message有效负载,该MultiValueMap值可能是字节数组、字符串或 Spring 的实例MultipartFile,具体取决于各个部分的内容类型.

MultipartResolver如果一个 bean 名称为multipartResolver(与 Spring 的预期名称相同),则 HTTP 入站端点在上下文中定位 a DispatcherServlet。如果它确实找到了该 bean,则在入站请求映射器上启用对多部分文件的支持。否则,它会在尝试将多部分文件请求映射到 Spring Integration 时失败Message。有关 Spring 对 的支持的更多信息MultipartResolver,请参阅Spring 参考手册

如果您希望将 a 代理multipart/form-data到另一台服务器,最好将其保留为原始形式。要处理这种情况,请不要将multipartResolverbean 添加到上下文中。将端点配置为期望byte[]请求,自定义消息转换器以包含 a ByteArrayHttpMessageConverter,并禁用默认的多部分转换器。您可能需要一些其他转换器来回复。以下示例显示了这种安排:

<int-http:inbound-gateway
                  channel="receiveChannel"
                  path="/inboundAdapter.htm"
                  request-payload-type="byte[]"
                  message-converters="converters"
                  merge-with-default-converters="false"
                  supported-methods="POST" />

<util:list id="converters">
    <beans:bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
    <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter" />
    <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</util:list>

当您向客户端发送响应时,您可以通过多种方式自定义网关的行为。默认情况下,网关通过发200回状态码来确认已收到请求。可以通过提供由 Spring MVC 解析的“viewName”来自定义此响应ViewResolver。如果网关应该期待对 的回复Message,您可以设置标志(构造函数参数)以使网关在创建 HTTP 响应之前expectReply等待回复。Message以下示例将网关配置为具有视图名称的 Spring MVC 控制器:

<bean id="httpInbound"
  class="org.springframework.integration.http.inbound.HttpRequestHandlingController">
  <constructor-arg value="true" /> <!-- indicates that a reply is expected -->
  <property name="requestChannel" ref="httpRequestChannel" />
  <property name="replyChannel" ref="httpReplyChannel" />
  <property name="viewName" value="jsonView" />
  <property name="supportedMethodNames" >
    <list>
      <value>GET</value>
      <value>DELETE</value>
    </list>
  </property>
</bean>

由于 的constructor-argtrue,它等待回复。前面的示例还显示了如何自定义网关接受的 HTTP 方法,默认情况下是POSTGET

回复消息在模型图中可用。默认情况下,该映射条目的键是“reply”,但您可以通过在端点配置上设置“replyKey”属性来覆盖此默认值。

有效载荷验证

从版本 5.2 开始,可以为 HTTP 入站端点提供 aValidator以在发送到通道之前检查有效负载。该有效载荷已经是转换和提取的结果,payloadExpression以缩小有价值数据的验证范围。验证失败处理与我们在 Spring MVC错误处理中的完全一样。

HTTP 出站组件

本节介绍 Spring Integration 的 HTTP 出站组件。

使用HttpRequestExecutingMessageHandler

要配置HttpRequestExecutingMessageHandler,请编写类似于以下内容的 bean 定义:

<bean id="httpOutbound"
  class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler">
  <constructor-arg value="http://localhost:8080/example" />
  <property name="outputChannel" ref="responseChannel" />
</bean>

这个 bean 定义通过委托给一个RestTemplate. 反过来,该模板委托给HttpMessageConverter实例列表以从有效负载生成 HTTP 请求正文Message。您可以配置这些转换器以及ClientHttpRequestFactory要使用的实例,如以下示例所示:

<bean id="httpOutbound"
  class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler">
  <constructor-arg value="http://localhost:8080/example" />
  <property name="outputChannel" ref="responseChannel" />
  <property name="messageConverters" ref="messageConverterList" />
  <property name="requestFactory" ref="customRequestFactory" />
</bean>

默认情况下,HTTP 请求是SimpleClientHttpRequestFactory使用 JDK的实例生成的HttpURLConnection。还支持使用 Apache Commons HTTP 客户端CommonsClientHttpRequestFactory,您可以将其注入(如前所示)。

对于出站网关,网关生成的回复消息包含请求消息中存在的所有消息头。

使用 Cookie

基本 cookie 支持由transfer-cookies出站网关上的属性提供。当设置为true(默认为false)时,Set-Cookie响应中从服务器接收到的标头将转换为Cookie回复消息中的标头。然后在后续发送中使用此标头。这可以实现简单的有状态交互,例如:

…​→logonGateway→…​→doWorkGateway→…​→logoffGateway→…​

如果transfer-cookiesfalse,则接收到的任何Set-Cookie标头都保留Set-Cookie在回复消息中,并在后续发送时被丢弃。

空响应体

HTTP 是一种请求-响应协议。但是,响应可能没有正文,只有标头。在这种情况下,无论提供什么,都会产生有效负载为 的HttpRequestExecutingMessageHandler回复。根据HTTP RFC 状态代码定义,有许多状态要求响应不得包含消息体(例如,)。在某些情况下,对同一 URL 的调用可能会或可能不会返回响应正文。例如,对 HTTP 资源的第一个请求返回内容,但第二个不返回(返回 a )。然而,在所有情况下,Messageorg.springframework.http.ResponseEntityexpected-response-type204 No Content304 Not Modifiedhttp_statusCode消息头已填充。这可以在 HTTP 出站网关之后的一些路由逻辑中使用。您还可以使用`<payload-type-router/>` 将带有 a 的消息路由ResponseEntity到与用于响应正文的流不同的流。

预期响应类型

除了前面关于空响应正文的说明,如果响应确实包含正文,则必须提供适当的expected-response-type属性,或者再次收到ResponseEntity没有正文的响应。expected-response-type必须与(配置的或默认的)实例HttpMessageConverterContent-Type响应中的标头兼容。这可以是一个抽象类,甚至是一个接口(例如java.io.Serializable当您使用 Java 序列化时Content-Type: application/x-java-serialized-object)。

从版本 5.5 开始,HttpRequestExecutingMessageHandler公开一个extractResponseBody标志(true默认情况下)以仅返回响应正文,或将整个ResponseEntity作为回复消息有效负载返回,独立于提供的expectedResponseType. 如果 body 中不存在,ResponseEntity则忽略此标志并ResponseEntity返回整体。

HTTP 命名空间支持

Spring Integration 提供了一个http命名空间和相应的模式定义。要将其包含在您的配置中,请在您的应用程序上下文配置文件中提供以下命名空间声明:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-http="http://www.springframework.org/schema/integration/http"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/http
    https://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
    ...
</beans>

入站

XML 命名空间提供了两个组件来处理 HTTP 入站请求:inbound-channel-adapterinbound-gateway. 为了在不返回专用响应的情况下处理请求,请使用inbound-channel-adapter. 以下示例显示了如何配置一个:

<int-http:inbound-channel-adapter id="httpChannelAdapter" channel="requests"
    supported-methods="PUT, DELETE"/>

要处理确实期望响应的请求,请使用inbound-gateway. 以下示例显示了如何配置一个:

<int-http:inbound-gateway id="inboundGateway"
    request-channel="requests"
    reply-channel="responses"/>

请求映射支持

Spring Integration 3.0 通过引入IntegrationRequestMappingHandlerMapping. 该实现依赖于 Spring Framework 3.1 或更高版本提供的增强 REST 支持。

HTTP 入站网关或 HTTP 入站通道适配器的解析会注册一个integrationRequestMappingHandlerMapping类型为 的 bean IntegrationRequestMappingHandlerMapping,以防尚未注册。这个特定的实现HandlerMapping将其逻辑委托给RequestMappingInfoHandlerMapping. 该实现提供类似于org.springframework.web.bind.annotation.RequestMappingSpring MVC 中的注解的功能。

有关详细信息,请参阅使用 映射请求@RequestMapping

为此,Spring Integration 3.0 引入了该<request-mapping>元素。您可以将此可选元素添加到<http:inbound-channel-adapter><http:inbound-gateway>。它与pathsupported-methods属性一起使用。以下示例显示如何在入站网关上配置它:

<inbound-gateway id="inboundController"
    request-channel="requests"
    reply-channel="responses"
    path="/foo/{fooId}"
    supported-methods="GET"
    view-name="foo"
    error-code="oops">
   <request-mapping headers="User-Agent"
     params="myParam=myValue"
     consumes="application/json"
     produces="!text/plain"/>
</inbound-gateway>

基于前面的配置,命名空间解析器创建一个实例IntegrationRequestMappingHandlerMapping(如果不存在)和一个HttpRequestHandlingControllerbean,并与它关联一个RequestMapping. RequestMapping反过来,这个实例被转换为 Spring MVC RequestMappingInfo

<request-mapping>元素提供以下属性:

  • headers

  • params

  • consumes

  • produces

使用或的pathsupported-methods属性,属性直接转换为Spring MVC 中注解提供的相应选项。<http:inbound-channel-adapter><http:inbound-gateway><request-mapping>org.springframework.web.bind.annotation.RequestMapping

<request-mapping>元素允许您将多个 Spring Integration HTTP 入站端点配置为相同path(甚至相同supported-methods),并允许您根据传入的 HTTP 请求提供不同的下游消息流。

或者,您也可以仅声明一个 HTTP 入站端点,并在 Spring 集成流程中应用路由和过滤逻辑以实现相同的结果。这使您可以Message尽早进入流程。以下示例显示了如何执行此操作:

<int-http:inbound-gateway request-channel="httpMethodRouter"
    supported-methods="GET,DELETE"
    path="/process/{entId}"
    payload-expression="#pathVariables.entId"/>

<int:router input-channel="httpMethodRouter" expression="headers.http_requestMethod">
    <int:mapping value="GET" channel="in1"/>
    <int:mapping value="DELETE" channel="in2"/>
</int:router>

<int:service-activator input-channel="in1" ref="service" method="getEntity"/>

<int:service-activator input-channel="in2" ref="service" method="delete"/>

有关处理程序映射的更多信息,请参阅Spring Framework Web Servlet 文档Spring Framework Web Reactive 文档

IntegrationRequestMappingHandlerMapping扩展了 Spring MVC类,继承了它 的RequestMappingHandlerMapping大部分逻辑,尤其是,当映射由于某种原因不匹配时handleNoMatch(Set, String, HttpServletRequest),它会为 HTTP 响应引发特定错误,从而阻止调用应用程序上下文中的任何剩余映射处理程序。4xx因此,不支持为 Spring Integration 和 Spring MVC 请求映射配置相同的路径(例如POST,在一个和GET另一个中);将找不到 MVC 映射..

跨域资源共享 (CORS) 支持

从 4.2 版开始,您可以使用元素配置<http:inbound-channel-adapter>and 。它表示与 Spring MVC的注解选项相同,并允许为 Spring Integration HTTP 端点配置跨域资源共享 (CORS):<http:inbound-gateway><cross-origin>@CrossOrigin@Controller

  • origin:允许的来源列表。这*意味着允许所有来源。这些值放置在Access-Control-Allow-Origin飞行前响应和实际响应的标题中。默认值为*

  • allowed-headers:指示在实际请求期间可以使用哪些请求标头。这*意味着允许客户端请求的所有标头。此属性控制飞行前响应Access-Control-Allow-Headers标头的值。默认值为*

  • exposed-headers:用户代理允许客户端访问的响应标头列表。此属性控制实际响应Access-Control-Expose-Headers标头的值。

  • method: 允许的 HTTP 请求方法:GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. 此处指定的方法会覆盖supported-methods.

  • allow-credentials:设置为true浏览器是否应包含与请求域相关联的任何 cookie,或者false是否不应包含。空字符串 ("") 表示未定义。如果true,则飞行前响应包括Access-Control-Allow-Credentials=true标头。默认值为true

  • max-age:控制飞行前响应的缓存持续时间。将此设置为合理的值可以减少浏览器所需的飞行前请求-响应交互的次数。此属性控制Access-Control-Max-Age飞行前响应中标头的值。值-1表示未定义。默认值为 1800 秒(30 分钟)。

CORS Java 配置由org.springframework.integration.http.inbound.CrossOrigin类表示,其实例可以注入到HttpRequestHandlingEndpointSupportbean 中。

响应状态代码

从 4.1 版开始,您可以配置<http:inbound-channel-adapter>status-code-expression覆盖默认200 OK状态。表达式必须返回一个可以转换为org.springframework.http.HttpStatus枚举值的对象。有evaluationContext一个BeanResolverand,从 5.1 版开始,RequestEntity<?>作为根对象提供。一个示例可能是在运行时解析一些返回状态代码值的作用域 bean。但是,最有可能的是,它被设置为一个固定值,例如status-code=expression="204"(无内容)或status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT". 默认status-code-expression为空,表示返回正常的“200 OK”响应状态。使用RequestEntity<?>作为根对象,状态代码可以是有条件的,例如请求方法、某些标头、URI 内容甚至请求正文。以下示例显示如何将状态代码设置为ACCEPTED

<http:inbound-channel-adapter id="inboundController"
       channel="requests" view-name="foo" error-code="oops"
       status-code-expression="T(org.springframework.http.HttpStatus).ACCEPTED">
   <request-mapping headers="BAR"/>
</http:inbound-channel-adapter>

从回复的标题中<http:inbound-gateway>解析“状态代码” 。从 4.2 版本开始,在. 有两种方法可以修改此行为:http_statusCodeMessagereply-timeout500 Internal Server Error

  • 添加一个reply-timeout-status-code-expression. status-code-expression这与入站适配器具有相同的语义。

  • 添加error-channel并返回带有 HTTP 状态代码标头的适当消息,如以下示例所示:

    <int:chain input-channel="errors">
        <int:header-enricher>
            <int:header name="http_statusCode" value="504" />
        </int:header-enricher>
        <int:transformer expression="payload.failedMessage" />
    </int:chain>

的有效载荷ErrorMessage是一个MessageTimeoutException. 它必须转换为可以由网关转换的东西,例如String. 一个很好的候选者是异常的 message 属性,它是您使用该expression技术时使用的值。

如果在主流超时后错误流超时,500 Internal Server Error则返回,或者,如果reply-timeout-status-code-expression存在,则对其进行评估。

以前,超时的默认状态代码是200 OK. 要恢复该行为,请设置reply-timeout-status-code-expression="200".

同样从版本 5.4 开始,在准备请求消息时遇到的错误将发送到错误通道(如果提供)。应该通过检查异常在错误流中做出关于引发适当异常的决定。以前,任何异常都被简单地抛出,导致 HTTP 500 服务器错误响应状态,但在某些情况下,问题可能是由不正确的请求参数引起的,因此ResponseStatusException应该抛出一个 4xx 客户端错误状态。有关ResponseStatusException更多信息,请参阅。ErrorMessage发送到此错误通道的包含原始异常作为有效负载进行分析。==== URI 模板变量和表达式

通过将path属性与payload-expression属性和header元素结合使用,您可以高度灵活地映射入站请求数据。

在以下示例配置中,入站通道适配器配置为使用以下 URI 接受请求:

/first-name/{firstName}/last-name/{lastName}

当您使用该payload-expression属性时,{firstName}URI 模板变量映射为Message有效负载,而{lastName}URI 模板变量映射到lname消息头,如以下示例中所定义:

<int-http:inbound-channel-adapter id="inboundAdapterWithExpressions"
    path="/first-name/{firstName}/last-name/{lastName}"
    channel="requests"
    payload-expression="#pathVariables.firstName">
    <int-http:header name="lname" expression="#pathVariables.lastName"/>
</int-http:inbound-channel-adapter>

有关 URI 模板变量的更多信息,请参阅Spring 参考手册中的uri 模板模式

从 Spring Integration 3.0 开始,除了有效载荷和标头表达式中可用的现有变量#pathVariables#requestParams变量外,我们还添加了其他有用的表达式变量:

  • #requestParams:MultiValueMapServletRequest parameterMap

  • #pathVariables: Mapfrom URI Template 占位符及其值。

  • #matrixVariablesMap根据Spring MVC 规范MultiValueMap的。请注意,这需要 Spring MVC 3.2 或更高版本。#matrixVariables

  • #requestAttributesorg.springframework.web.context.request.RequestAttributes与当前请求关联的。

  • #requestHeadersorg.springframework.http.HttpHeaders来自当前请求的对象。

  • #cookies:来自当前请求Map<String, Cookie>的实例。javax.servlet.http.Cookie

ThreadLocal org.springframework.web.context.request.RequestAttributes请注意,如果该消息流是单线程的并且位于请求线程中,则所有这些值(和其他值)都可以通过变量在下游消息流的表达式中访问。以下示例配置使用expression属性的转换器:

<int-:transformer
    expression="T(org.springframework.web.context.request.RequestContextHolder).
                  requestAttributes.request.queryString"/>

出境

要配置出站网关,您可以使用命名空间支持。以下代码片段显示出站 HTTP 网关的可用配置选项:

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="http://localhost/test"
    http-method="POST"
    extract-request-payload="false"
    expected-response-type="java.lang.String"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

最重要的是,请注意提供了“http-method”和“expected-response-type”属性。这是两个最常配置的值。默认http-methodPOST,默认响应类型为 null。对于 null 响应类型,只要其 HTTP 状态为成功(不成功的状态代码会引发异常) ,响应的负载就会Message包含。ResponseEntity如果您希望使用不同的类型,例如 a String,请将其提供为完全限定的类名(java.lang.String在前面的示例中)。另请参阅有关HTTP Outbound Components中的空响应主体的说明。

从 Spring Integration 2.1 开始,request-timeoutHTTP 出站网关的属性被重命名为reply-timeout以更好地反映其意图。

从 Spring Integration 2.2 开始,默认情况下不再启用基于 HTTP 的 Java 序列化。以前,将expected-response-type属性设置为Serializable对象时,Accept未正确设置标头。自 Spring Integration 2.2 以来,SerializingHttpMessageConverter现在已更新将Accept标头设置为application/x-java-serialized-object.

但是,由于这可能导致与现有应用程序不兼容,因此决定不再将此转换器自动添加到 HTTP 端点。如果您希望使用 Java 序列化,您可以通过使用属性(当您使用 XML 配置时)或使用方法(在 Java 配置中)将其添加SerializingHttpMessageConverter到适当的端点。或者,您可能希望考虑改用 JSON,这是通过将 Jackson 库放在类路径中来启用的。message-converterssetMessageConverters()

http-method-expression从 Spring Integration 2.2 开始,您还可以使用 SpEL 和属性动态确定 HTTP 方法。请注意,此属性与 互斥http-method。您还可以使用该expected-response-type-expression属性代替expected-response-type并提供任何确定响应类型的有效 SpEL 表达式。以下配置示例使用expected-response-type-expression

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="http://localhost/test"
    http-method-expression="headers.httpMethod"
    extract-request-payload="false"
    expected-response-type-expression="payload"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

如果要以单向方式使用出站适配器,则可以使用 anoutbound-channel-adapter来代替。这意味着执行成功的响应时无需向回复通道发送任何消息。在任何不成功的响应状态码的情况下,它都会抛出异常。配置看起来与网关非常相似,如以下示例所示:

<int-http:outbound-channel-adapter id="example"
    url="http://localhost/example"
    http-method="GET"
    channel="requests"
    charset="UTF-8"
    extract-payload="false"
    expected-response-type="java.lang.String"
    request-factory="someRequestFactory"
    order="3"
    auto-startup="false"/>

要指定 URL,您可以使用“url”属性或“url-expression”属性。'url' 属性采用一个简单的字符串(带有 URI 变量的占位符,如下所述)。'url-expression' 是一个 SpEL 表达式,以Message为根对象,它启用动态 url。表达式求值产生的 URL 仍然可以包含 URI 变量的占位符。

在以前的版本中,一些用户使用占位符将整个 URL 替换为 URI 变量。Spring 3.1 中的更改可能会导致转义字符出现一些问题,例如“?”。因此,如果您希望完全在运行时生成 URL,我们建议您使用“url-expression”属性。

映射 URI 变量

如果您的 URL 包含 URI 变量,您可以使用uri-variable元素映射它们。此元素可用于 HTTP 出站网关和 HTTP 出站通道适配器。以下示例将zipCodeURI 变量映射到表达式:

<int-http:outbound-gateway id="trafficGateway"
    url="https://local.yahooapis.com/trafficData?appid=YdnDemo&amp;zip={zipCode}"
    request-channel="trafficChannel"
    http-method="GET"
    expected-response-type="java.lang.String">
    <int-http:uri-variable name="zipCode" expression="payload.getZip()"/>
</int-http:outbound-gateway>

uri-variable元素定义了两个属性:nameexpression。该name属性标识 URI 变量的名称,而该expression属性用于设置实际值。通过使用该expression属性,您可以利用 Spring 表达式语言 (SpEL) 的全部功能,它使您可以完全动态地访问消息负载和消息头。例如,在前面的配置中,getZip()在 的有效负载对象上调用该方法,Message并且该方法的结果用作名为“zipCode”的 URI 变量的值。

从 Spring Integration 3.0 开始,HTTP 出站端点支持该uri-variables-expression属性来指定expression应评估的值,从而在MapURL 模板中生成所有 URI 变量占位符。它提供了一种机制,您可以根据出站消息使用不同的变量表达式。<uri-variable/>此属性与元素互斥。以下示例显示了如何使用该uri-variables-expression属性:

<int-http:outbound-gateway
     url="https://foo.host/{foo}/bars/{bar}"
     request-channel="trafficChannel"
     http-method="GET"
     uri-variables-expression="@uriVariablesBean.populate(payload)"
     expected-response-type="java.lang.String"/>

uriVariablesBean可以定义如下:

public class UriVariablesBean {
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    public Map<String, ?> populate(Object payload) {
        Map<String, Object> variables = new HashMap<String, Object>();
        if (payload instanceOf String.class)) {
            variables.put("foo", "foo"));
        }
        else {
            variables.put("foo", EXPRESSION_PARSER.parseExpression("headers.bar"));
        }
        return variables;
    }

}
必须评估uri-variables-expressionMap. 的值Map必须是Stringor的实例Expression。通过在 outbound 的上下文中使用这些表达式,这Map将提供给URI 变量占位符的进一步解析。 ExpressionEvalMapMessage

重要的

uriVariablesExpression属性为评估 URI 变量提供了一种非常强大的机制。我们预计人们大多使用简单的表达方式,例如前面的示例。但是,您也可以配置一些东西,例如"@uriVariablesBean.populate(#root)"在返回的映射中使用表达式作为variables.put("thing1", EXPRESSION_PARSER.parseExpression(message.getHeaders().get("thing2", String.class)));,其中表达式在名为 的消息头中动态提供thing2。由于标头可能来自不受信任的来源,因此SimpleEvaluationContext在评估这些表达式时使用 HTTP 出站端点。SimpleEvaluationContext仅使用 SpEL 功能的一个子集。如果您信任您的消息源并希望使用受限 SpEL 构造,请将trustedSpel出站端点的属性设置为true.

url-expression通过使用自定义和一些实用程序来构建和编码 URL 参数,您可以实现需要为每个消息提供一组动态 URI 变量的方案。以下示例显示了如何执行此操作:

url-expression="T(org.springframework.web.util.UriComponentsBuilder)
                           .fromHttpUrl('https://HOST:PORT/PATH')
                           .queryParams(payload)
                           .build()
                           .toUri()"

queryParams()方法需要 aMultiValueMap<String, String>作为参数,因此您可以在执行请求之前提前构建一组真实的 URL 查询参数。

整体queryString也可以表示为uri-variable,如以下示例所示:

<int-http:outbound-gateway id="proxyGateway" request-channel="testChannel"
              url="http://testServer/test?{queryString}">
    <int-http:uri-variable name="queryString" expression="'a=A&amp;b=B'"/>
</int-http:outbound-gateway>

在这种情况下,您必须手动提供 URL 编码。例如,您可以将org.apache.http.client.utils.URLEncodedUtils#format()用于此目的。如前所述,可以使用以下 Java Streams 片段将手动构建的方法MultiValueMap<String, String>转换为List<NameValuePair> format()方法参数:

List<NameValuePair> nameValuePairs =
    params.entrySet()
            .stream()
            .flatMap(e -> e
                    .getValue()
                    .stream()
                    .map(v -> new BasicNameValuePair(e.getKey(), v)))
            .collect(Collectors.toList());

控制 URI 编码

默认情况下,UriComponentsBuilder在发送请求之前,URL 字符串会被编码(参见 )为 URI 对象。在某些使用非标准 URI(例如 RabbitMQ REST API)的场景中,不希望执行编码。和<http:outbound-gateway/>提供<http:outbound-channel-adapter/>一个encoding-mode属性。要禁用 URL 编码,请将此属性设置为NONE(默认为TEMPLATE_AND_VALUES)。如果您希望对某些 URL 进行部分编码,请在 a 中使用expressiona <uri-variable/>,如以下示例所示:

<http:outbound-gateway url="https://somehost/%2f/fooApps?bar={param}" encoding-mode="NONE">
          <http:uri-variable name="param"
            expression="T(org.apache.commons.httpclient.util.URIUtil)
                                             .encodeWithinQuery('Hello World!')"/>
</http:outbound-gateway>

使用 Java DSL,此选项可由BaseHttpMessageHandlerSpec.encodingMode()选项控制。相同的配置适用于WebFlux 模块Web 服务模块中的类似出站组件。对于非常复杂的场景,建议UriTemplateHandler在外部提供的上配置一个RestTemplate;或者在 WebFlux 的情况下 -WebClient使用它UriBuilderFactory

使用 Java 配置 HTTP 端点

以下示例显示如何使用 Java 配置入站网关:

示例 1. 使用 Java 配置的入站网关
@Bean
public HttpRequestHandlingMessagingGateway inbound() {
    HttpRequestHandlingMessagingGateway gateway =
        new HttpRequestHandlingMessagingGateway(true);
    gateway.setRequestMapping(mapping());
    gateway.setRequestPayloadType(String.class);
    gateway.setRequestChannelName("httpRequest");
    return gateway;
}

@Bean
public RequestMapping mapping() {
    RequestMapping requestMapping = new RequestMapping();
    requestMapping.setPathPatterns("/foo");
    requestMapping.setMethods(HttpMethod.POST);
    return requestMapping;
}

以下示例显示如何使用 Java DSL 配置入站网关:

示例 2. 使用 Java DSL 的入站网关
@Bean
public IntegrationFlow inbound() {
    return IntegrationFlows.from(Http.inboundGateway("/foo")
            .requestMapping(m -> m.methods(HttpMethod.POST))
            .requestPayloadType(String.class))
        .channel("httpRequest")
        .get();
}

以下示例显示如何使用 Java 配置出站网关:

示例 3. 使用 Java 配置的出站网关
@ServiceActivator(inputChannel = "httpOutRequest")
@Bean
public HttpRequestExecutingMessageHandler outbound() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler("http://localhost:8080/foo");
    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(String.class);
    return handler;
}

以下示例显示如何使用 Java DSL 配置出站网关:

示例 4. 使用 Java DSL 的出站网关
@Bean
public IntegrationFlow outbound() {
    return IntegrationFlows.from("httpOutRequest")
        .handle(Http.outboundGateway("http://localhost:8080/foo")
            .httpMethod(HttpMethod.POST)
            .expectedResponseType(String.class))
        .get();
}

超时处理

在 HTTP 组件的上下文中,必须考虑两个计时区域:

  • 与 Spring 集成通道交互时的超时

  • 与远程 HTTP 服务器交互时的超时

组件与消息通道交互,可以指定超时时间。例如,HTTP 入站网关将从连接的 HTTP 客户端收到的消息转发到消息通道(使用请求超时),因此 HTTP 入站网关从回复通道(使用回复超时)接收回复消息,该回复消息用于生成 HTTP 响应。下图提供了直观的解释:

http入站网关
图 1. 超时设置如何应用于 HTTP 入站网关

对于出站端点,我们需要考虑与远程服务器交互时的计时方式。下图显示了这种情况:

http出站网关
图 2. 超时设置如何应用于 HTTP 出站网关

在使用 HTTP 出站网关或 HTTP 出站通道适配器发出活动 HTTP 请求时,您可能需要配置与 HTTP 相关的超时行为。在这些情况下,这两个组件使用 Spring 的RestTemplate支持来执行 HTTP 请求。

要为 HTTP 出站网关和 HTTP 出站通道适配器配置超时,您可以RestTemplate直接引用 bean(通过使用rest-template属性),也可以提供对ClientHttpRequestFactorybean 的引用(通过使用request-factory属性)。Spring 提供了以下ClientHttpRequestFactory接口的实现:

如果您没有显式配置request-factoryorrest-template属性,则会实例化默认值RestTemplate(使用 a )。SimpleClientHttpRequestFactory

对于某些 JVM 实现,URLConnection类对超时的处理可能不一致。

例如,来自 Java™ 平台标准版 6 API 规范setConnectTimeout

此方法的一些非标准实现可能会忽略指定的超时。要查看连接超时设置,请调用 getConnectTimeout()。

如果你有特定的需求,你应该测试你的超时。考虑使用HttpComponentsClientHttpRequestFactory,而后者又使用Apache HttpComponents HttpClient而不是依赖于 JVM 提供的实现。

当您将 Apache HttpComponents HttpClient 与池连接管理器一起使用时,您应该知道,默认情况下,连接管理器为每个给定路由创建不超过两个并发连接,并且总共不超过 20 个连接。对于许多现实世界的应用程序,这些限制可能被证明过于约束。有关配置此重要组件的信息,请参阅Apache 文档

SimpleClientHttpRequestFactory以下示例使用分别配置为连接和读取超时 5 秒的配置 HTTP 出站网关:

<int-http:outbound-gateway url="https://samples.openweathermap.org/data/2.5/weather?q={city}"
                           http-method="GET"
                           expected-response-type="java.lang.String"
                           request-factory="requestFactory"
                           request-channel="requestChannel"
                           reply-channel="replyChannel">
    <int-http:uri-variable name="city" expression="payload"/>
</int-http:outbound-gateway>

<bean id="requestFactory"
      class="org.springframework.http.client.SimpleClientHttpRequestFactory">
    <property name="connectTimeout" value="5000"/>
    <property name="readTimeout"    value="5000"/>
</bean>

HTTP 出站网关

对于HTTP Outbound Gateway,XML Schema 只定义了reply-timeout回复超时映射到org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler类的sendTimeout属性。更准确地说,属性是在扩展类上设置的,它最终将属性设置在.AbstractReplyProducingMessageHandlerMessagingTemplate

sendTimeout属性的值默认为“-1”,并将应用于已连接的MessageChannel. 这意味着,根据实现,消息通道的发送方法可能会无限期地阻塞。此外,仅当实际 MessageChannel 实现具有阻塞发送(例如 'full' bounded QueueChannel)时,才使用sendTimeout属性。

HTTP 入站网关

对于 HTTP 入站网关,XML Schema 定义了request-timeout属性,用于设置类(扩展类)的requestTimeout属性。您还可以使用该属性映射到同一类的属性。HttpRequestHandlingMessagingGatewayMessagingGatewaySupportreply-timeoutreplyTimeout

两个超时属性的默认值为1000ms(一千毫秒或一秒)。最终,该request-timeout属性用于sendTimeoutMessagingTemplate实例上设置 。replyTimeout另一方面,该属性用于设置实例的receiveTimeout属性。MessagingTemplate

要模拟连接超时,您可以连接到不可路由的 IP 地址,例如 10.255.255.10。

HTTP 代理配置

如果您在代理后面并且需要为 HTTP 出站适配器或网关配置代理设置,您可以应用两种方法之一。在大多数情况下,您可以依赖控制代理设置的标准 Java 系统属性。否则,您可以为 HTTP 客户端请求工厂实例显式配置 Spring bean。

标准 Java 代理配置

您可以设置三个系统属性来配置 HTTP 协议处理程序使用的代理设置:

  • http.proxyHost: 代理服务器的主机名。

  • http.proxyPort:端口号(默认为80)。

  • http.nonProxyHosts:应该绕过代理直接访问的主机列表。这是一个由 分隔的模式列表|。模式可以以通配符开头或结尾*。任何与这些模式之一匹配的主机都可以通过直接连接而不是代理来访问。

对于 HTTPS,以下属性可用:

  • https.proxyHost: 代理服务器的主机名。

  • https.proxyPort:端口号,默认为80。

Spring的SimpleClientHttpRequestFactory

如果您需要对代理配置进行更明确的控制,可以使用 SpringSimpleClientHttpRequestFactory并配置其“代理”属性,如以下示例所示:

<bean id="requestFactory"
    class="org.springframework.http.client.SimpleClientHttpRequestFactory">
    <property name="proxy">
        <bean id="proxy" class="java.net.Proxy">
            <constructor-arg>
                <util:constant static-field="java.net.Proxy.Type.HTTP"/>
            </constructor-arg>
            <constructor-arg>
                <bean class="java.net.InetSocketAddress">
                    <constructor-arg value="123.0.0.1"/>
                    <constructor-arg value="8080"/>
                </bean>
            </constructor-arg>
        </bean>
    </property>
</bean>

HTTP 标头映射

Spring Integration 支持 HTTP 请求和 HTTP 响应的 HTTP 标头映射。

默认情况下,所有标准HTTP 标头都从消息映射到 HTTP 请求或响应标头,无需进一步配置。但是,如果您确实需要进一步定制,您可以利用命名空间支持来提供额外的配置。您可以提供以逗号分隔的标头名称列表,并且可以包含简单的模式,其中“*”字符充当通配符。提供此类值会覆盖默认行为。基本上,它假设您在那时完全控制。但是,如果您确实想包含所有标准 HTTP 标头,则可以使用快捷模式:HTTP_REQUEST_HEADERSHTTP_RESPONSE_HEADERS. 以下清单显示了两个示例(第一个使用通配符):

<int-http:outbound-gateway id="httpGateway"
    url="http://localhost/test2"
    mapped-request-headers="thing1, thing2"
    mapped-response-headers="X-*, HTTP_RESPONSE_HEADERS"
    channel="someChannel"/>

<int-http:outbound-channel-adapter id="httpAdapter"
    url="http://localhost/test2"
    mapped-request-headers="thing1, thing2, HTTP_REQUEST_HEADERS"
    channel="someChannel"/>

适配器和网关使用DefaultHttpHeaderMapper,它现在为入站和出站适配器提供了两种静态工厂方法,以便可以应用正确的方向(根据需要将 HTTP 请求和响应映射入或出)。

如果需要进一步定制,也可以独立配置一个,通过属性DefaultHttpHeaderMapper注入到适配器中。header-mapper

在 5.0 版之前,DefaultHttpHeaderMapper用户定义的非标准 HTTP 标头的默认前缀是X-. 5.0 版将默认前缀更改为空字符串。根据RFC-6648,现在不鼓励使用此类前缀。您仍然可以通过设置DefaultHttpHeaderMapper.setUserDefinedHeaderPrefix()属性来自定义此选项。以下示例为 HTTP 网关配置标头映射器:

<int-http:outbound-gateway id="httpGateway"
    url="http://localhost/test2"
    header-mapper="headerMapper"
    channel="someChannel"/>

<bean id="headerMapper" class="o.s.i.http.support.DefaultHttpHeaderMapper">
    <property name="inboundHeaderNames" value="thing1*, *thing2, thing3"/>
    <property name="outboundHeaderNames" value="a*b, d"/>
</bean>

如果你需要做一些除了DefaultHttpHeaderMapper支持之外的事情,你可以HeaderMapper直接实现策略接口并为你的实现提供参考。

集成图控制器

从 4.3 版开始,HTTP 模块提供了一个@EnableIntegrationGraphController配置类注解和一个<int-http:graph-controller/>XML 元素以将其公开IntegrationGraphServer为 REST 服务。有关详细信息,请参阅集成图

HTTP 示例

本节通过几个示例总结了我们对 Spring Integration 的 HTTP 支持的介绍。

多部分 HTTP 请求 - RestTemplate(客户端)和 Http 入站网关(服务器)

这个例子展示了使用 Spring 发送多部分 HTTP 请求RestTemplate并使用 Spring Integration HTTP 入站适配器接收它是多么简单。我们创建一个MultiValueMap并用多部分数据填充它。通过RestTemplate将其转换为MultipartHttpServletRequest. 此特定客户端发送包含公司名称和图像文件(公司徽标)的多部分 HTTP 请求。以下清单显示了该示例:

RestTemplate template = new RestTemplate();
String uri = "http://localhost:8080/multipart-http/inboundAdapter.htm";
Resource s2logo =
   new ClassPathResource("org/springframework/samples/multipart/spring09_logo.png");
MultiValueMap map = new LinkedMultiValueMap();
map.add("company", "SpringSource");
map.add("company-logo", s2logo);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("multipart", "form-data"));
HttpEntity request = new HttpEntity(map, headers);
ResponseEntity<?> httpResponse = template.exchange(uri, HttpMethod.POST, request, null);

这就是我们为客户所需要的。

在服务器端,我们有以下配置:

<int-http:inbound-channel-adapter id="httpInboundAdapter"
    channel="receiveChannel"
    path="/inboundAdapter.htm"
    supported-methods="GET, POST"/>

<int:channel id="receiveChannel"/>

<int:service-activator input-channel="receiveChannel">
    <bean class="org.springframework.integration.samples.multipart.MultipartReceiver"/>
</int:service-activator>

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

'httpInboundAdapter' 接收请求并将其转换为Message具有有效负载的LinkedMultiValueMap. 然后我们在“multipartReceiver”服务激活器中解析它,如以下示例所示:

public void receive(LinkedMultiValueMap<String, Object> multipartRequest){
    System.out.println("### Successfully received multipart request ###");
    for (String elementName : multipartRequest.keySet()) {
        if (elementName.equals("company")){
            System.out.println("\t" + elementName + " - " +
                ((String[]) multipartRequest.getFirst("company"))[0]);
        }
        else if (elementName.equals("company-logo")){
            System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
                ((UploadedMultipartFile) multipartRequest
                    .getFirst("company-logo")).getOriginalFilename());
        }
    }
}

您应该看到以下输出:

### Successfully received multipart request ###
   company - SpringSource
   company-logo - as UploadedMultipartFile: spring09_logo.png

1. see XML Configuration