HTTP 命名空间支持

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

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

入站

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

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

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

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

请求映射支持

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

HTTP 入站网关或 HTTP 入站通道适配器的解析会在尚未注册的情况下注册一个integrationRequestMappingHandlerMapping bean(类型为IntegrationRequestMappingHandlerMapping)。这种HandlerMapping的特定实现将其逻辑委托给RequestMappingInfoHandlerMapping。此实现提供与 Spring MVC 中的org.springframework.web.bind.annotation.RequestMapping注释类似的功能。

有关更多信息,请参见使用@RequestMapping映射请求

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

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

根据前面的配置,命名空间解析器会创建一个IntegrationRequestMappingHandlerMapping实例(如果不存在)和一个HttpRequestHandlingController bean,并将其与RequestMapping的实例关联。然后,这个RequestMapping实例会被转换为 Spring MVC RequestMappingInfo

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

  • headers

  • params

  • consumes

  • produces

使用<http:inbound-channel-adapter><http:inbound-gateway>pathsupported-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 版本开始,您可以使用<cross-origin>元素配置<http:inbound-channel-adapter><http:inbound-gateway>。它表示与 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 请求方法:GETPOSTHEADOPTIONSPUTPATCHDELETETRACE。此处指定的Method会覆盖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 版本开始,您可以使用status-code-expression配置<http:inbound-channel-adapter>以覆盖默认的200 OK状态。表达式必须返回可以转换为org.springframework.http.HttpStatus枚举值的對象。evaluationContext具有BeanResolver,从 5.1 版本开始,它以RequestEntity<?>作为根对象提供。一个示例可能是运行时解析返回状态码值的某些作用域 bean。但是,最有可能的是,它被设置为固定值,例如status-code=expression="204"(无内容)或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>从回复Messagehttp_statusCode标头解析“状态码”。从 4.2 版本开始,在reply-timeout内未收到回复时的默认响应状态码为500 内部服务器错误。有两种方法可以修改此行为

  • 添加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。一个合适的候选者是异常的消息属性,这是使用expression技术时使用的值。

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

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

同样从5.4版本开始,在准备请求消息时遇到的错误将发送到错误通道(如果提供)。应该通过检查异常在错误流程中做出关于抛出适当异常的决定。以前,任何异常都会被简单地抛出,导致HTTP 500服务器错误响应状态,但在某些情况下,问题可能是由不正确的请求参数引起的,因此应该改为抛出带有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模板模式

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

  • #requestParams:来自ServletRequest parameterMapMultiValueMap

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

  • #matrixVariables:根据Spring MVC规范MultiValueMapMap。请注意,#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="https://127.0.0.1/test"
    http-method="POST"
    extract-request-payload="false"
    expected-response-type="java.lang.String"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

最重要的是,请注意提供了“http-method”和“expected-response-type”属性。这两个是最常用的配置值。默认的http-methodPOST,默认的响应类型为空。使用空响应类型时,回复Message的有效负载包含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,这可以通过在类路径上拥有Jackson库来启用。

从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="https://127.0.0.1/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="https://127.0.0.1/example"
    http-method="GET"
    channel="requests"
    charset="UTF-8"
    extract-payload="false"
    expected-response-type="java.lang.String"
    request-factory="someRequestFactory"
    order="3"
    auto-startup="false"/>

要指定URL,您可以使用“url”属性或“url-expression”属性。“url”属性采用简单的字符串(带有URI变量的占位符,如下所述)。“url-expression”是一个SpEL表达式,其中Message作为根对象,这使得动态URL成为可能。表达式求值结果的URL仍然可以具有URI变量的占位符。

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

映射URI变量

如果您的URL包含URI变量,您可以使用uri-variable元素对其进行映射。此元素可用于HTTP出站网关和HTTP出站通道适配器。以下示例将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元素定义两个属性:nameexpressionname属性标识URI变量的名称,而expression属性用于设置实际值。通过使用expression属性,您可以利用Spring表达式语言(SpEL)的全部功能,这使您可以完全动态地访问消息有效负载和消息头。例如,在前面的配置中,会在Message的有效负载对象上调用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;
    }

}
uri-variables-expression必须计算为MapMap的值必须是StringExpression的实例。此Map将提供给ExpressionEvalMap,以便通过在出站Message的上下文中使用这些表达式来进一步解析URI变量占位符。

重要提示:uriVariablesExpression属性为评估URI变量提供了一种非常强大的机制。我们预计人们大多使用简单的表达式,例如前面的示例。但是,您还可以配置诸如"@uriVariablesBean.populate(#root)"之类的内容,其中返回的映射中的表达式为variables.put("thing1", EXPRESSION_PARSER.parseExpression(message.getHeaders().get("thing2", String.class)));,其中表达式动态地提供在名为thing2的消息头中。由于标头可能来自不受信任的来源,因此HTTP出站端点在评估这些表达式时使用SimpleEvaluationContextSimpleEvaluationContext仅使用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()。如前所述,可以使用以下Java Streams代码段将手动构建的MultiValueMap<String, String>转换为List<NameValuePair> format()方法参数

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

控制URI编码

默认情况下,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的情况下,使用其UriBuilderFactory配置WebClient