HTTP 命名空间支持
Spring Integration 提供了 http 命名空间和相应的 Schema 定义。
要将其包含在您的配置中,请在应用程序上下文配置文件中提供以下命名空间声明:
<?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 Integration 3.0 通过引入 |
HTTP 入站网关或 HTTP 入站通道适配器的解析会注册一个类型为 IntegrationRequestMappingHandlerMapping 的 integrationRequestMappingHandlerMapping bean,如果尚未注册的话。
HandlerMapping 的这个特定实现将其逻辑委托给 RequestMappingInfoHandlerMapping。
该实现提供了类似于 Spring MVC 中 org.springframework.web.bind.annotation.RequestMapping 注解的功能。
|
有关更多信息,请参阅 使用 |
为此,Spring Integration 3.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 实例关联。
此 RequestMapping 实例又会转换为 Spring MVC 的 RequestMappingInfo。
<request-mapping> 元素提供以下属性:
-
headers -
params -
consumes -
produces
结合 <http:inbound-channel-adapter> 或 <http:inbound-gateway> 的 path 和 supported-methods 属性,<request-mapping> 属性直接转换为 Spring MVC 中 org.springframework.web.bind.annotation.RequestMapping 注解提供的相应选项。
<request-mapping> 元素允许您将多个 Spring Integration HTTP 入站端点配置到相同的 path(甚至相同的 supported-methods),并允许您根据传入的 HTTP 请求提供不同的下游消息流。
或者,您也可以只声明一个 HTTP 入站端点,并在 Spring Integration 流中应用路由和过滤逻辑以达到相同的结果。
这允许您尽早将 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> 和 <http:inbound-gateway> 配置 <cross-origin> 元素。
它代表与 Spring MVC @Controller 注解的 @CrossOrigin 相同的选项,并允许为 Spring Integration HTTP 端点配置跨域资源共享 (CORS):
-
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 枚举值的对象。
evaluationContext 具有 BeanResolver,并且从 5.1 版开始,将 RequestEntity<?> 作为根对象提供。
一个示例可能是在运行时解析某个范围的 bean,该 bean 返回状态码值。
然而,最有可能的是,它被设置为固定值,例如 status-code=expression="204" (No Content),或 status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"。
默认情况下,status-code-expression 为 null,这意味着返回正常的“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> 从回复 Message 的 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 的 payload 是一个 MessageTimeoutException。
它必须转换为网关可以转换的东西,例如 String。
一个很好的选择是异常的消息属性,这是您使用 expression 技术时使用的值。
如果错误流在主流程超时后超时,则返回 500 Internal Server Error,或者,如果存在 reply-timeout-status-code-expression,则对其进行评估。
|
以前,超时的默认状态码是 |
同样从 5.4 版开始,在准备请求消息时遇到的错误会发送到错误通道(如果提供)。
关于抛出适当异常的决定应在错误流中通过检查异常来完成。
以前,任何异常都会简单地抛出,导致 HTTP 500 服务器错误响应状态,但在某些情况下,问题可能是由不正确的请求参数引起的,因此应抛出带有 4xx 客户端错误状态的 ResponseStatusException。
有关更多信息,请参阅 ResponseStatusException。
发送到此错误通道的 ErrorMessage 包含原始异常作为 payload 以供分析。
URI 模板变量和表达式
通过结合使用 path 属性、payload-expression 属性和 header 元素,您可以高度灵活地映射入站请求数据。
在以下示例配置中,入站通道适配器配置为接受使用以下 URI 的请求:
/first-name/{firstName}/last-name/{lastName}
当您使用 payload-expression 属性时,{firstName} URI 模板变量映射为 Message payload,而 {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 起,除了 payload 和 header 表达式中可用的现有 #pathVariables 和 #requestParams 变量外,我们还添加了其他有用的表达式变量:
-
#requestParams:来自ServletRequestparameterMap的MultiValueMap。 -
#pathVariables:来自 URI 模板占位符及其值的Map。 -
#matrixVariables:根据 Spring MVC 规范 的MultiValueMap的Map。 请注意,#matrixVariables需要 Spring MVC 3.2 或更高版本。 -
#requestAttributes:与当前请求关联的org.springframework.web.context.request.RequestAttributes。 -
#requestHeaders:来自当前请求的org.springframework.http.HttpHeaders对象。 -
#cookies:来自当前请求的jakarta.servlet.http.Cookie实例的MultiValueMap<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。
如果响应类型为 null,则回复 Message 的 payload 包含 ResponseEntity,只要其 HTTP 状态为成功(非成功状态码会抛出异常)。
如果您期望不同的类型,例如 String,请将其作为完全限定类名提供(在前面的示例中为 java.lang.String)。
另请参阅 HTTP 出站组件 中关于空响应体的注意事项。
从 Spring Integration 2.1 开始,HTTP 出站网关的 request-timeout 属性已重命名为 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 序列化,可以通过使用 message-converters 属性(当您使用 XML 配置时)或使用 setMessageConverters() 方法(在 Java 配置中)将 SerializingHttpMessageConverter 添加到相应的端点。
或者,您可能希望考虑使用 JSON,通过在 classpath 中包含 Jackson 库 来启用 JSON。
从 Spring Integration 2.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 表达式,以 |
映射 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 表达式语言 (SpEL) 的强大功能,该功能允许您完全动态访问消息 payload 和消息头。
例如,在前面的配置中,在 Message 的 payload 对象上调用 getZip() 方法,该方法的结果用作名为“zipCode”的 URI 变量的值。
自 Spring Integration 3.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;
}
}
|
|
重要
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)为 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 Services 模块 中的类似出站组件。
对于更复杂的场景,建议在外部提供的 RestTemplate 上配置 UriTemplateHandler;或者在 WebFlux 的情况下,为 WebClient 配置 UriBuilderFactory。