http.md 58.6 KB
Newer Older
dallascao's avatar
dallascao 已提交
1 2
# HTTP 支持

3
## http 支持
dallascao's avatar
dallascao 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Spring 集成的 HTTP 支持允许运行 HTTP 请求和处理入站 HTTP 请求。HTTP 支持由以下网关实现组成:`HttpInboundEndpoint``HttpRequestExecutingMessageHandler`。另见[WebFlux 支持](./webflux.html#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`依赖项。

27
### HTTP 入站组件
dallascao's avatar
dallascao 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

要通过 HTTP 接收消息,需要使用 HTTP 入站通道适配器或 HTTP 入站网关。为了支持 HTTP 入站适配器,它们需要部署在 Servlet 容器中,例如[Apache Tomcat](https://tomcat.apache.org/)[Jetty](https://www.eclipse.org/jetty/)。最简单的方法是使用 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](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/remoting.html),这是 Spring 框架参考文档的一部分。

如果你正在 Spring MVC 应用程序中运行,那么前面提到的显式 Servlet 定义是不必要的。在这种情况下,网关的 Bean 名称可以与 URL 路径匹配,就像对 Spring MVC 控制器 Bean 匹配一样。有关更多信息,请参见[Web MVC 框架](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc),这是 Spring 框架参考文档的一部分。

|   |有关示例应用程序和相应的配置,请参见[Spring Integration Samples](https://github.com/spring-projects/spring-integration-samples)存储库。<br/>它包含[HTTP 示例](https://github.com/spring-projects/spring-integration-samples/tree/main/basic/http)应用程序,它演示了 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](https://docs.spring.io/spring-integration/api/index.html)。一个额外的标志(`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,则在入站请求映射器上启用了对多部分文件的支持。,否则,<br/>,当它试图将多部分文件请求映射到 Spring 集成`Message`时,它将失败。<br/>有关 Spring 对`MultipartResolver`的支持的更多信息,请参见[Spring Reference Manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart)。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |如果你希望将`multipart/form-data`代理到另一台服务器,最好是将其保持为原始形式。<br/>要处理这种情况,请不要将`multipartResolver` Bean 添加到上下文中。<br/>将端点配置为期望一个`byte[]`请求,自定义消息转换器以包括`ByteArrayHttpMessageConverter`,并禁用默认的多部分转换器。<br/>你可能需要其他一些转换器来进行回复。<br/>下面的示例显示了这样的安排:<br/><br/>```<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 MVC`ViewResolver`解析的“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”属性来覆盖此缺省项。

89
#### 有效载荷验证
dallascao's avatar
dallascao 已提交
90 91 92

从版本 5.2 开始,HTTP 入站端点可以提供`Validator`,以在发送到通道之前检查有效负载。这个有效负载已经是`payloadExpression`之后的转换和提取的结果,以缩小关于有价值数据的验证范围。验证失败处理与 Spring MVC[错误处理](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-exceptionhandlers)中的处理完全相同。

93
### HTTP 出站组件
dallascao's avatar
dallascao 已提交
94 95 96

本节描述 Spring 集成的 HTTP 出站组件。

97
#### 使用`HttpRequestExecutingMessageHandler`
dallascao's avatar
dallascao 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

要配置`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`的实例生成的,该实例使用 JDK`HttpURLConnection`。还通过`CommonsClientHttpRequestFactory`支持使用 ApacheCommons HTTP 客户机,你可以插入它(如前面所示)。

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

126
#### 使用 cookies
dallascao's avatar
dallascao 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

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

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

如果`transfer-cookies``false`,则接收到的任何`Set-Cookie`头在回复消息中保持为`Set-Cookie`,并在随后的发送中被删除。

|   |空响应主体<br/><br/>http 是一个请求-响应协议。<br/>但是,响应可能没有主体,只有标题。<br/>在这种情况下,`Message`产生一个有效负载为`org.springframework.http.ResponseEntity`的回复`expected-response-type`,而不管是否提供了<br/>根据<gt r=“139”,有许多状态要求响应不能包含消息体(例如,`204 No Content`)。<br/>也有对相同 URL 的调用可能返回或不返回响应体的情况,<br/>例如,对 HTTP 资源的第一个请求返回内容,但是第二个不是(返回`304 Not Modified`)。但是,在所有情况下,<br/>`http_statusCode`消息头被填充。<br/>这可以在 HTTP 出站网关之后的某些路由逻辑中使用。<br/>你还可以使用`\<payload-type-router/\>`将带有`ResponseEntity`的消息路由到不同的流,而不是用于带有主体的响应。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

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

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

142
### http 名称空间支持
dallascao's avatar
dallascao 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

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>
```

163
#### 入站
dallascao's avatar
dallascao 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

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"/>
```

180
#### 请求映射支持
dallascao's avatar
dallascao 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

|   |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](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html)[the Spring Framework Web Reactive documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html)

|   |`IntegrationRequestMappingHandlerMapping`扩展了 Spring MVC`RequestMappingHandlerMapping`类,继承了它的大部分逻辑,尤其是`handleNoMatch(Set, String, HttpServletRequest)`,当映射由于某种原因不匹配时,它会为 HTTP 响应抛出一个特定的`4xx`错误,防止调用应用程序上下文中的任何剩余映射处理程序。<br/>因此,为 Spring 集成和 Spring MVC 请求映射(例如,不支持`POST`in one 和`GET`in the other);将找不到 MVC 映射。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

246
#### 跨源资源共享支持
dallascao's avatar
dallascao 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

从版本 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 中。

264
#### 响应状态代码
dallascao's avatar
dallascao 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

从版本 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`<br/>要恢复该行为,请设置`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 模板模式](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-uri-templates)

Spring Integration3.0 之后,除了现有的`#pathVariables``#requestParams`变量在有效负载和报头表达式中可用之外,我们还添加了其他有用的表达式变量:

* `#requestParams`:来自`ServletRequest``MultiValueMap``parameterMap`

* `#pathVariables`:来自 URI 模板占位符的`Map`及其值。

* `#matrixVariables`:根据[Spring MVC Specification](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-matrix-variables)`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"/>
```

343
#### 出站
dallascao's avatar
dallascao 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

要配置出站网关,你可以使用名称空间支持。以下代码片段显示了出站 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 出站组件](#http-outbound)中关于空响应主体的注释。

|   |从 Spring Integration2.1 开始,HTTP 出站网关的`request-timeout`属性被重命名为`reply-timeout`,以更好地反映其意图。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |自 Spring Integration2.2 起,默认情况下不再启用 HTTP 上的 Java 序列化。<br/>以前,在将`expected-response-type`属性设置为`Serializable`对象时,`Accept`头未正确设置。<br/>自 Spring Integration2.2 起,`SerializingHttpMessageConverter`现在已经更新,将`Accept`头设置为`application/x-java-serialized-object`<br/><br/>但是,由于这可能会导致与现有应用程序不兼容,因此决定不再自动将此转换器添加到 HTTP 端点。<br/>如果你希望使用 Java 序列化,你可以通过使用`message-converters`属性(当你使用 XML 配置时)或通过使用`setMessageConverters()`方法(在 Java 配置中)将`SerializingHttpMessageConverter`添加到适当的端点,<br/>或者,你也可以考虑使用 JSON,这是通过在 Classpath 上具有[Jackson 图书馆](https://github.com/FasterXML/jackson)来实现的。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

从 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”属性。<br/>“url”属性接受一个简单的字符串(对于 URI 变量有占位符,如下所述)。<br/>“url-expression”是一个 SPEL 表达式,以`Message`作为根对象,<br/>表达式求值结果的 URL 仍然可以有 URI 变量的占位符。<br/><br/>在以前的版本中,一些用户使用占位符用 URI 变量替换整个 URL,<br/> Spring 3.1 中的更改可能会导致转义字符的一些问题,例如“?”。<br/>出于这个原因,我们建议,如果你希望在运行时完全生成 URL,那么可以使用“url-expression”属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

401
#### 映射 URI 变量
dallascao's avatar
dallascao 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488

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

```
<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`元素定义了两个属性:`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`<br/>`Map`的值必须是`String``Expression`的实例。<br/>`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&amp;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());
```

489
#### 控制 URI 编码
dallascao's avatar
dallascao 已提交
490 491 492 493 494 495 496 497 498 499 500 501 502

默认情况下,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 模块](./webflux.html#webflux)[Web 服务模块](./ws.html#ws)中的类似出站组件。对于非常复杂的场景,建议在外部提供的`RestTemplate`上配置`UriTemplateHandler`;或者在 WebFlux-`WebClient`的情况下配置`UriBuilderFactory`

503
### 用 Java 配置 HTTP 端点
dallascao's avatar
dallascao 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

下面的示例展示了如何使用 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();
}
```

575
### 超时处理
dallascao's avatar
dallascao 已提交
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

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

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

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

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

![HTTP 入站网关](images/http-inbound-gateway.png)

图 1。超时设置如何应用到 HTTP 入站网关

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

![HTTP 出站网关](images/http-outbound-gateway.png)

图 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):uses[Apache HttpComponents HttpClient](https://hc.apache.org/httpcomponents-client-ga/)(自 Spring 3.1 起)

如果你没有显式地配置`request-factory``rest-template`属性,那么将实例化一个默认的`RestTemplate`(它使用`SimpleClientHttpRequestFactory`)。

|   |对于某些 JVM 实现,`URLConnection`类对超时的处理可能不一致。<br/><br/>例如,来自 Java 平台,关于`setConnectTimeout`的标准版 6API 规范:<br/><br/>>该方法的一些非标准实现可能会忽略指定的超时。<br/>要查看连接超时集,请调用 getConnectTimeout()。<br/><br/>如果你有特定的需求,则应该测试你的超时。<gt r="<449"/>考虑使用,这反过来,使用[Apache HttpComponents HttpClient](https://hc.apache.org/httpcomponents-client-ga/),而不是依赖 JVM 提供的实现。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |当你使用带有池连接管理器的 Apache HttpComponents HttpClient 时,你应该意识到,默认情况下,连接管理器为每个给定的路由创建的并发连接不超过两个,并且总共创建的连接不超过 20 个,对于许多现实世界中的应用程序,<br/>,这些限制可能被证明是过于约束。<br/>有关配置这个重要组件的信息,请参见[Apache 文档](https://hc.apache.org/httpcomponents-client-ga/)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

下面的示例通过使用`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)时使用。

636
#### HTTP 入站网关
dallascao's avatar
dallascao 已提交
637 638 639 640 641 642 643 644

对于 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。|
|---|-----------------------------------------------------------------------------------------------------|

645
### HTTP 代理配置
dallascao's avatar
dallascao 已提交
646 647 648

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

649
#### 标准 Java 代理配置
dallascao's avatar
dallascao 已提交
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666

你可以设置三个系统属性来配置 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](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html)

667
####  Spring 的`SimpleClientHttpRequestFactory`
dallascao's avatar
dallascao 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

如果需要对代理配置进行更明确的控制,可以使用 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>
```

690
### HTTP 标头映射
dallascao's avatar
dallascao 已提交
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728

Spring 集成为 HTTP 请求和 HTTP 响应提供了对 HTTP 头映射的支持。

默认情况下,所有标准[HTTP 头](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)都从消息映射到 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](https://tools.ietf.org/html/rfc6648),现在不鼓励使用这种前缀。你仍然可以通过设置`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`策略接口,并提供对你的实现的引用。

729
### 积分图控制器
dallascao's avatar
dallascao 已提交
730 731 732

从版本 4.3 开始,HTTP 模块提供了`@EnableIntegrationGraphController`配置类注释和`<int-http:graph-controller/>`XML 元素,以将`IntegrationGraphServer`作为 REST 服务公开。有关更多信息,请参见[积分图](./graph.html#integration-graph)

733
### http 样本
dallascao's avatar
dallascao 已提交
734 735 736

本节用几个例子来概括我们对 Spring Integration 的 HTTP 支持的介绍。

737
#### 多部分 HTTP 请求—RESTTemplate(客户端)和 HTTP 入站网关(服务器)
dallascao's avatar
dallascao 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800

这个示例展示了用 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
```