# HTTP 支持
# http 支持
Spring 集成的 HTTP 支持允许运行 HTTP 请求和处理入站 HTTP 请求。HTTP 支持由以下网关实现组成:HttpInboundEndpoint
和HttpRequestExecutingMessageHandler
。另见WebFlux 支持。
你需要在项目中包含此依赖项:
Maven
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-http</artifactId>
<version>5.5.9</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-http:5.5.9"
必须在目标 Servlet 容器上提供javax.servlet:javax.servlet-api
依赖项。
# HTTP 入站组件
要通过 HTTP 接收消息,需要使用 HTTP 入站通道适配器或 HTTP 入站网关。为了支持 HTTP 入站适配器,它们需要部署在 Servlet 容器中,例如Apache Tomcat (opens new window)或Jetty (opens new window)。最简单的方法是使用 Spring 的[HttpRequestHandlerServlet
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/context/support/httprequesthandlerservlet.html),在web.xml
文件中提供以下 Servlet 定义:
<servlet>
<servlet-name>inboundGateway</servlet-name>
<servlet-class>o.s.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
请注意, Servlet 名称与 Bean 名称匹配。有关使用HttpRequestHandlerServlet
的更多信息,请参见Remoting and web services using Spring (opens new window),这是 Spring 框架参考文档的一部分。
如果你正在 Spring MVC 应用程序中运行,那么前面提到的显式 Servlet 定义是不必要的。在这种情况下,网关的 Bean 名称可以与 URL 路径匹配,就像对 Spring MVC 控制器 Bean 匹配一样。有关更多信息,请参见Web MVC 框架 (opens new window),这是 Spring 框架参考文档的一部分。
有关示例应用程序和相应的配置,请参见Spring Integration Samples (opens new window)存储库。 它包含HTTP 示例 (opens new window)应用程序,它演示了 Spring 集成的 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
的映射。默认转换器封装了简单的策略,这些策略(例如)为POST
请求创建String
消息,其中内容类型以text
开头。有关详细信息,请参见Javadoc (opens new window)。一个额外的标志(mergeWithDefaultConverters
)可以与自定义HttpMessageConverter
的列表一起设置,以在自定义转换器之后添加默认转换器。默认情况下,此标志设置为false
,这意味着自定义转换器替换了默认列表。
消息转换过程使用(可选的)requestPayloadType
属性和传入的Content-Type
头。从版本 4.3 开始,如果请求没有内容类型头,则假定application/octet-stream
,如RFC 2616
所建议的那样。此前,这类信息的主体被忽略了。
Spring 集成 2.0 实现了对多部分文件的支持。如果请求被包装为MultipartHttpServletRequest
,当你使用默认转换器时,该请求将被转换为Message
有效负载,该负载是MultiValueMap
,其中包含的值可能是字节数组、字符串或 Spring 的MultipartFile
实例,取决于各个部分的内容类型。
如果 Bean 的名称为multipartResolver (与 Spring 的DispatcherServlet 预期的名称相同),则 HTTP 入站端点在上下文中定位MultipartResolver 。,如果确实定位到 Bean,则在入站请求映射器上启用了对多部分文件的支持。,否则,,当它试图将多部分文件请求映射到 Spring 集成 Message 时,它将失败。有关 Spring 对 MultipartResolver 的支持的更多信息,请参见Spring Reference Manual (opens new window)。 |
---|
如果你希望将multipart/form-data 代理到另一台服务器,最好是将其保持为原始形式。要处理这种情况,请不要将 multipartResolver Bean 添加到上下文中。将端点配置为期望一个 byte[] 请求,自定义消息转换器以包括ByteArrayHttpMessageConverter ,并禁用默认的多部分转换器。你可能需要其他一些转换器来进行回复。 下面的示例显示了这样的安排: <br/><int-http:inbound-gateway<br/> channel="receiveChannel"<br/> path="/inboundAdapter.htm"<br/> request-payload-type="byte[]"<br/> message-converters="converters"<br/> merge-with-default-converters="false"<br/> supported-methods="POST" /><br/><br/><util:list id="converters"><br/> <beans:bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /><br/> <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter" /><br/> <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /><br/></util:list><br/> |
---|
当你向客户机发送响应时,你有许多方法来定制网关的行为。默认情况下,网关通过返回200
状态代码来确认请求已被接收。可以通过提供一个由 Spring MVCViewResolver
解析的“ViewName”来定制此响应。如果网关希望得到对Message
的答复,则可以设置expectReply
标志(构造函数参数),使网关在创建 HTTP 响应之前等待答复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-arg
的值true
,它将等待回复。前面的示例还显示了如何定制网关接受的 HTTP 方法,默认情况下是POST
和GET
。
回复消息可在模型图中获得。默认情况下,该映射条目的键是“replyKey”,但是你可以通过在端点的配置上设置“replyKey”属性来覆盖此缺省项。
# 有效载荷验证
从版本 5.2 开始,HTTP 入站端点可以提供Validator
,以在发送到通道之前检查有效负载。这个有效负载已经是payloadExpression
之后的转换和提取的结果,以缩小关于有价值数据的验证范围。验证失败处理与 Spring MVC错误处理 (opens new window)中的处理完全相同。
# HTTP 出站组件
本节描述 Spring 集成的 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 这个定义通过将 HTTP 请求委托给RestTemplate
来运行。然后,该模板将委托给HttpMessageConverter
实例的列表,以便从Message
有效负载生成 HTTP 请求主体。你可以配置这些转换器以及要使用的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
的实例生成的,该实例使用 JDKHttpURLConnection
。还通过CommonsClientHttpRequestFactory
支持使用 ApacheCommons HTTP 客户机,你可以插入它(如前面所示)。
对于出站网关,网关产生的回复消息包含请求消息中存在的所有消息头。 |
---|
# 使用 cookies
出站网关上的transfer-cookies
属性提供了基本的 cookie 支持。当设置为true
(默认值为false
)时,从服务器接收到的响应中的Set-Cookie
头将在回复消息中转换为Cookie
。然后在后续的发送中使用这个头。这使得能够进行简单的有状态交互,例如:
…→logonGateway→…→doWorkGateway→…→logoffGateway→…
如果transfer-cookies
是false
,则接收到的任何Set-Cookie
头在回复消息中保持为Set-Cookie
,并在随后的发送中被删除。
空响应主体 http 是一个请求-响应协议。 但是,响应可能没有主体,只有标题。 在这种情况下, Message 产生一个有效负载为org.springframework.http.ResponseEntity 的回复expected-response-type ,而不管是否提供了根据<gtr=“139”,有许多状态要求响应不能包含消息体(例如, 204 No Content )。也有对相同 URL 的调用可能返回或不返回响应体的情况, 例如,对 HTTP 资源的第一个请求返回内容,但是第二个不是(返回 304 Not Modified )。但是,在所有情况下,, http_statusCode 消息头被填充。这可以在 HTTP 出站网关之后的某些路由逻辑中使用。 你还可以使用 \<payload-type-router/\> 将带有ResponseEntity 的消息路由到不同的流,而不是用于带有主体的响应。 |
---|
expected-response-type 除了前面提到的关于空响应主体的说明外,如果响应确实包含主体,则必须提供一个适当的 expected-response-type 属性,或者,同样,你收到的是一个没有正文的ResponseEntity 。expected-response-type 必须与(配置或默认的)HttpMessageConverter 实例和响应中的Content-Type 头兼容。这可以是一个抽象类,甚至是一个接口(当你使用 Java 序列化时,例如 java.io.Serializable )和Content-Type: application/x-java-serialized-object )。 |
---|
从版本 5.5 开始,HttpRequestExecutingMessageHandler
公开了一个extractResponseBody
标志(默认情况下是true
),以仅返回响应主体,或返回整个ResponseEntity
作为回复消息的有效负载,独立于所提供的expectedResponseType
。如果ResponseEntity
中不存在主体,则忽略此标志,并返回整个ResponseEntity
。
# http 名称空间支持
Spring 集成提供了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-adapter
和inbound-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 Integration3.0 通过引入[](https://DOCS. Spring.io/ Spring-Integration/API/org/SpringFramework/Integration/HTTP/Inbound/IntegrationRequestMappinghandlerMapping.html)改进了 REST 支持。实现依赖于 Spring Framework3.1 或更高版本提供的增强型 REST 支持。 |
---|
HTTP 入站网关或 HTTP 入站通道适配器的解析注册了类型为[integrationRequestMappingHandlerMapping
Bean 的IntegrationRequestMappingHandlerMapping
](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/http/integrationrequestmappinghandlermapping.html),以防其中一个尚未注册。[HandlerMapping
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/ Servlet/handlermapping.html)的这种特定实现将其逻辑委托给[RequestMappingInfoHandlerMapping
](https://DOCS. Spring.io/ Spring//DOCS/current/javadoc-api/org/springframework/ Servlet///mvc/methandlermappingmappingmapping.html)。该实现提供了类似于 Spring MVC 中的[org.springframework.web.bind.annotation.RequestMapping
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/bind/annotation/requestmapping.html)注释的功能。
有关更多信息,请参见[mapping requests with@RequestMapping ](https://DOCS. Spring.io/ Spring/DOCS/current/ Spring-framework-reference/web.html#mvc-ann-requestmapping)。 |
---|
为此, Spring Integration3.0 引入了<request-mapping>
元素。可以将此可选元素添加到<http:inbound-channel-adapter>
和<http:inbound-gateway>
中。它与path
和supported-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
(如果不存在)和HttpRequestHandlingController
Bean 的实例,并与之关联[RequestMapping
](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/http/inbound/requestmapping.html)的实例。这个RequestMapping
实例依次转换为 Spring MVC[RequestMappingInfo
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/ Servlet/mvc/method/requestmappinginfo.html)。
<request-mapping>
元素提供了以下属性:
headers
params
consumes
produces
使用path
和supported-methods
的<http:inbound-channel-adapter>
属性或<http:inbound-gateway>
,<request-mapping>
属性直接转换为 Spring MVC 中org.springframework.web.bind.annotation.RequestMapping
注释提供的相应选项。
<request-mapping>
元素允许你将几个 Spring 集成 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"/>
有关处理程序映射的更多信息,请参见the Spring Framework Web Servlet documentation (opens new window)或the Spring Framework Web Reactive documentation (opens new window)。
IntegrationRequestMappingHandlerMapping 扩展了 Spring MVCRequestMappingHandlerMapping 类,继承了它的大部分逻辑,尤其是handleNoMatch(Set, String, HttpServletRequest) ,当映射由于某种原因不匹配时,它会为 HTTP 响应抛出一个特定的4xx 错误,防止调用应用程序上下文中的任何剩余映射处理程序。因此,为 Spring 集成和 Spring MVC 请求映射(例如,不支持 POST in one 和GET in the other);将找不到 MVC 映射。 |
---|
# 跨源资源共享支持
从版本 4.2 开始,你可以使用<cross-origin>
元素配置<http:inbound-channel-adapter>
和<http:inbound-gateway>
。它表示与 Spring MVC 的@CrossOrigin
用于@Controller
注释的选项相同的选项,并允许为 Spring 集成 HTTP 端点配置跨源资源共享:
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
:如果浏览器应该包括与请求的域相关联的任何 cookie,则设置为true
;如果不应该,则设置为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
类表示,其实例可以注入到HttpRequestHandlingEndpointSupport
bean 中。
# 响应状态代码
从版本 4.1 开始,你可以将<http:inbound-channel-adapter>
配置为status-code-expression
,以覆盖默认的200 OK
状态。表达式必须返回一个可以转换为org.springframework.http.HttpStatus
enum 值的对象。evaluationContext
具有BeanResolver
,并且从版本 5.1 开始,提供了作为根对象的RequestEntity<?>
。一个例子可能是在运行时解析某个范围 Bean,该范围返回一个状态代码值。然而,最有可能的是,它被设置为固定值,如status-code=expression="204"
(没有内容),或status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"
。默认情况下,status-code-expression
为 null,这意味着返回正常的’200OK’响应状态。使用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>
解析来自回复http_statusCode
头的“状态代码”。从版本 4.2 开始,当reply-timeout
中没有收到答复时,默认的响应状态代码是500 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
。一个好的候选者是 Exception 的消息属性,这是使用expression
技术时使用的值。
如果错误流在主流超时之后超时,则返回500 Internal Server Error
,或者,如果存在reply-timeout-status-code-expression
,则对其进行求值。
以前,超时的默认状态代码是200 OK 。要恢复该行为,请设置 reply-timeout-status-code-expression="200" 。 |
---|
同样从版本 5.4 开始,在准备请求消息时遇到的错误将被发送到错误通道(如果提供的话)。关于抛出适当异常的决定应该通过检查异常在错误流中完成。以前,任何异常都只是简单地抛出,从而导致 HTTP500 服务器错误响应状态,但在某些情况下,该问题可能是由不正确的请求参数引起的,因此应该抛出具有 4xx 客户端错误状态的ResponseStatusException
。有关更多信息,请参见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 模板模式 (opens new window)。
Spring Integration3.0 之后,除了现有的#pathVariables
和#requestParams
变量在有效负载和报头表达式中可用之外,我们还添加了其他有用的表达式变量:
#requestParams
:来自ServletRequest
的MultiValueMap``parameterMap
。#pathVariables
:来自 URI 模板占位符的Map
及其值。#matrixVariables
:根据Spring MVC Specification (opens new window)的Map
。注意#matrixVariables
需要 Spring MVC3.2 或更高。#requestAttributes
:与当前请求关联的org.springframework.web.context.request.RequestAttributes
。#requestHeaders
:当前请求中的org.springframework.http.HttpHeaders
对象。#cookies
:来自当前请求的javax.servlet.http.Cookie
实例的Map<String, 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-method
是POST
,默认的响应类型是 null。对于空响应类型,只要其 HTTP 状态是成功的(不成功的状态代码抛出异常),响应Message
的有效负载就包含ResponseEntity
。如果你希望使用不同的类型,例如String
,那么将其作为一个完全限定的类名提供(在前面的示例中是java.lang.String
)。另请参见HTTP 出站组件中关于空响应主体的注释。
从 Spring Integration2.1 开始,HTTP 出站网关的request-timeout 属性被重命名为reply-timeout ,以更好地反映其意图。 |
---|
自 Spring Integration2.2 起,默认情况下不再启用 HTTP 上的 Java 序列化。 以前,在将 expected-response-type 属性设置为Serializable 对象时,Accept 头未正确设置。自 Spring Integration2.2 起, SerializingHttpMessageConverter 现在已经更新,将Accept 头设置为application/x-java-serialized-object 。但是,由于这可能会导致与现有应用程序不兼容,因此决定不再自动将此转换器添加到 HTTP 端点。 如果你希望使用 Java 序列化,你可以通过使用 message-converters 属性(当你使用 XML 配置时)或通过使用setMessageConverters() 方法(在 Java 配置中)将SerializingHttpMessageConverter 添加到适当的端点,或者,你也可以考虑使用 JSON,这是通过在 Classpath 上具有Jackson 图书馆 (opens new window)来实现的。 |
---|
从 Spring Integration2.2 开始,你还可以通过使用 SPEL 和http-method-expression
属性动态地确定 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"/>
如果要以单向方式使用出站适配器,则可以使用outbound-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 仍然可以有 URI 变量的占位符。 在以前的版本中,一些用户使用占位符用 URI 变量替换整个 URL, Spring 3.1 中的更改可能会导致转义字符的一些问题,例如“?”。 出于这个原因,我们建议,如果你希望在运行时完全生成 URL,那么可以使用“url-expression”属性。 |
---|
# 映射 URI 变量
如果你的 URL 包含 URI 变量,那么你可以使用uri-variable
元素来映射它们。此元素可用于 HTTP 出站网关和 HTTP 出站通道适配器。下面的示例将zipCode
URI 变量映射到一个表达式:
<int-http:outbound-gateway id="trafficGateway"
url="https://local.yahooapis.com/trafficData?appid=YdnDemo&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
元素定义了两个属性:name
和expression
。name
属性标识 URI 变量的名称,而expression
属性用于设置实际值。通过使用expression
属性,你可以利用 Spring 表达式语言的全部功能,该语言为你提供了对消息有效负载和消息头的完全动态访问。例如,在前面的配置中,在Message
的有效负载对象上调用getZip()
方法,该方法的结果被用作名为“zipcode”的 URI 变量的值。
Spring Integration3.0 之后,HTTP 出站端点支持uri-variables-expression
属性,以指定一个应该进行求值的expression
,从而在 URL 模板内的所有 URI 变量占位符中产生一个Map
。它提供了一种机制,你可以根据出站消息使用不同的变量表达式。这个属性与<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-expression 必须求值为Map 。Map 的值必须是String 或Expression 的实例。此 Map 是提供给ExpressionEvalMap 的通过在出站Message 的上下文中使用这些表达式来进一步解析 URI 变量占位符。 |
---|
IMPORTANT
uriVariablesExpression
属性为评估 URI 变量提供了一个非常强大的机制。我们预计人们通常使用简单的表达式,例如前面的示例。但是,你也可以配置诸如"@uriVariablesBean.populate(#root)"
之类的内容,其中返回的映射中的表达式为variables.put("thing1", EXPRESSION_PARSER.parseExpression(message.getHeaders().get("thing2", String.class)));
,其中该表达式在名为thing2
的消息头中动态提供。由于头文件可能来自不受信任的源文件,HTTP 出站端点在计算这些表达式时使用SimpleEvaluationContext
。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()
方法需要MultiValueMap<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&b=B'"/>
</int-http:outbound-gateway>
在这种情况下,你必须手动提供 URL 编码。例如,你可以为此目的使用org.apache.http.client.utils.URLEncodedUtils#format()
。如前所述,手动构建的MultiValueMap<String, String>
可以通过使用以下 Java Streams 片段转换为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 编码
默认情况下,URL 字符串在发送请求之前被编码(参见[UriComponentsBuilder
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/util/uricomponentsbuilder.html))到 URI 对象。在使用非标准 URI(例如 RabbitMQ REST API)的某些场景中,不希望执行编码。<http:outbound-gateway/>
和<http:outbound-channel-adapter/>
提供了一个encoding-mode
属性。要禁用对 URL 进行编码,请将此属性设置为NONE
(默认情况下,它是TEMPLATE_AND_VALUES
)。如果希望对某些 URL 进行部分编码,请在<uri-variable/>
中使用expression
,如下例所示:
<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 服务模块中的类似出站组件。对于非常复杂的场景,建议在外部提供的RestTemplate
上配置UriTemplateHandler
;或者在 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 响应的应答通道(使用应答超时)接收应答消息。下面的插图提供了一个直观的解释:
图1.超时设置如何应用到 HTTP 入站网关
对于出站端点,我们需要考虑与远程服务器交互时的计时工作方式。下图显示了这种情况:
图2.超时设置如何应用到 HTTP 出站网关
在使用 HTTP 出站网关或 HTTP 出站通道适配器发出活动 HTTP 请求时,你可能希望配置与 HTTP 相关的超时行为。在这些实例中,这两个组件使用 Spring 的[RestTemplate
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/client/resttemplate.html)支持来执行 HTTP 请求。
要为 HTTP 出站网关和 HTTP 出站通道适配器配置超时,你可以直接引用RestTemplate
Bean(通过使用rest-template
属性),也可以提供对[ClientHttpRequestFactory
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/http/client/clienthtprequestfactory.html) Bean(通过使用<gtr="429"/>r=“429”属性)的引用。 Spring 提供了以下ClientHttpRequestFactory
接口的实现方式:
[
SimpleClientHttpRequestFactory
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/http/client/simpleclienthtprequestfactory.html):使用标准的 J2SE 工具进行 HTTP 请求[
HttpComponentsClientHttpRequestFactory
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/http/client/httpcomponentsclienthtprequestfactory.html):usesApache HttpComponents HttpClient (opens new window)(自 Spring 3.1 起)
如果你没有显式地配置request-factory
或rest-template
属性,那么将实例化一个默认的RestTemplate
(它使用SimpleClientHttpRequestFactory
)。
对于某些 JVM 实现,URLConnection 类对超时的处理可能不一致。例如,来自 Java 平台,关于 setConnectTimeout 的标准版 6API 规范:>该方法的一些非标准实现可能会忽略指定的超时。 要查看连接超时集,请调用 getConnectTimeout()。 如果你有特定的需求,则应该测试你的超时。<gtr="<449"/>考虑使用,这反过来,使用Apache HttpComponents HttpClient (opens new window),而不是依赖 JVM 提供的实现。 |
---|
当你使用带有池连接管理器的 Apache HttpComponents HttpClient 时,你应该意识到,默认情况下,连接管理器为每个给定的路由创建的并发连接不超过两个,并且总共创建的连接不超过 20 个,对于许多现实世界中的应用程序, ,这些限制可能被证明是过于约束。 有关配置这个重要组件的信息,请参见Apache 文档 (opens new window)。 |
---|
下面的示例通过使用SimpleClientHttpRequestFactory
配置 HTTP 出站网关,该网关配置的连接和读取超时分别为 5 秒:
<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 出站网关,XML 模式只定义了回复-超时。回复-超时映射到发送超时类的org.springframework.integration.http.outtout.HttprequestExecutingMessageHandler属性。更准确地说,该属性是在扩展的AbstractReplyProducingMessageHandler
类上设置的,它最终将该属性设置在MessagingTemplate
上。
发送超时属性的值默认为“-1”,并将应用于连接的MessageChannel
。这意味着,根据实现方式,消息通道的发送方法可能无限期地阻塞。此外,发送超时属性仅在实际的 MessageChannel 实现具有阻塞发送(例如’full’bounded queuechannel)时使用。
# HTTP 入站网关
对于 HTTP 入站网关,XML 模式定义了request-timeout
属性,该属性用于在HttpRequestHandlingMessagingGateway
类上(在扩展的MessagingGatewaySupport
类上)设置requestTimeout
属性。你还可以使用reply-timeout
属性映射到同一个类上的replyTimeout
属性。
这两个超时属性的默认值都是1000ms
(1000 毫秒或 1 秒)。最终,request-timeout
属性用于在MessagingTemplate
实例上设置sendTimeout
。另一方面,replyTimeout
属性用于在MessagingTemplate
实例上设置receiveTimeout
属性。
要模拟连接超时,可以连接到不可路由的 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。
有关更多信息,请参见https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html (opens new window)
# Spring 的SimpleClientHttpRequestFactory
如果需要对代理配置进行更明确的控制,可以使用 Spring 的SimpleClientHttpRequestFactory
并配置其’代理’属性,如下例所示:
<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 集成为 HTTP 请求和 HTTP 响应提供了对 HTTP 头映射的支持。
默认情况下,所有标准HTTP 头 (opens new window)都从消息映射到 HTTP 请求或响应头,而无需进一步配置。但是,如果确实需要进一步定制,则可以利用名称空间支持来提供额外的配置。你可以提供一个以逗号分隔的头名称列表,还可以包括简单的模式,其中“*”字符充当通配符。提供这样的值可以重写默认行为。基本上,它假设你在那一点上完全处于控制之中。但是,如果你确实希望包含所有标准的 HTTP 头,则可以使用快捷模式:HTTP_REQUEST_HEADERS
和HTTP_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 (opens new window),现在不鼓励使用这种前缀。你仍然可以通过设置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 的RestTemplate
发送多部分 HTTP 请求并用 Spring 集成 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