Spring integration @MessagingGateway、Spring Cloud Stream和跨两者的错误处理

Spring integration @MessagingGateway、Spring Cloud Stream和跨两者的错误处理,spring-integration,spring-cloud-stream,Spring Integration,Spring Cloud Stream,关于发布的答案,处理@MessagingGateway上可以从Spring云流服务返回的错误的正确方法是什么 总而言之,我有一个@MessagingGateway,它提供对使用SpringCloudStream构建的异步服务的同步访问。当我的Spring Cloud Stream服务层中发生错误时,我会创建一个错误响应,并通过SubscribableChannel将其发送给处理错误的其他@StreamListener服务 例如,创建帐户时,我会向accountCreated频道发送一条消息。发生

关于发布的答案,处理@MessagingGateway上可以从Spring云流服务返回的错误的正确方法是什么

总而言之,我有一个@MessagingGateway,它提供对使用SpringCloudStream构建的异步服务的同步访问。当我的Spring Cloud Stream服务层中发生错误时,我会创建一个错误响应,并通过SubscribableChannel将其发送给处理错误的其他@StreamListener服务

例如,创建帐户时,我会向
accountCreated
频道发送一条消息。发生错误时,我会向
accountNotCreated
频道发送错误响应

这很好,但我还希望向@MessagingGateway的客户端发送错误响应,以便它们同步接收错误响应。@MessagingGateway注释具有
errorChannel
属性,但@Gateway注释没有。因此,@MessagingGateway的客户端应该能够阻止并等待1)创建帐户或2)错误响应

同样,这里的目标是构建“后端”服务,利用Spring Cloud Stream提供事务性服务(即创建、更新或删除数据的服务),同时为我们的客户提供阻止并等待响应返回的“网关”访问。Artem Bilan为我提供的解决方案适用于快乐之路,但当出现错误时,我不清楚Spring集成如何最适合处理这个问题

使用代码示例更新

GatewayApplication.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.dsl.HeaderEnricherSpec;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@EnableBinding({GatewayApplication.GatewayChannels.class})
@SpringBootApplication
public class GatewayApplication {

  @Component
  public interface GatewayChannels {
    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Output(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @MessagingGateway
  public interface StreamGateway {
    public static final String ENRICH = "enrich";

    @Gateway(requestChannel = ENRICH, replyChannel = GatewayChannels.TO_UPPERCASE_REPLY)
    StringWrapper process(StringWrapper payload) throws MyException;
  }

  @RestController
  public class UppercaseController {
    @Autowired
    StreamGateway gateway;

    @GetMapping(value = "/string/{string}",
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
    public ResponseEntity<StringWrapper> getUser(@PathVariable("string") String string) {
      try {
        StringWrapper result = gateway.process(new StringWrapper(string));
        // Instead of catching the exception in the below catch clause, here we have just a string
        // representation of the stack trace when an exception occurs.
        return new ResponseEntity<StringWrapper>(result, HttpStatus.OK);
      } catch (MyException e) {
        // Why is the exception not caught here?
        return new ResponseEntity<StringWrapper>(new StringWrapper("An error has occurred"),
            HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
  }

  @Bean
  public IntegrationFlow headerEnricherFlow() {
    return IntegrationFlows.from(StreamGateway.ENRICH)
        .enrichHeaders(HeaderEnricherSpec::headerChannelsToString)
        .channel(GatewayChannels.TO_UPPERCASE_REQUEST).get();
  }

}
package com.example.demo;

import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

@EnableBinding({CloudStreamApplication.CloudStreamChannels.class})
@SpringBootApplication
public class CloudStreamApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Output(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Input(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @Component
  public class Processor {

    @Autowired
    CloudStreamChannels channels;

    @StreamListener(CloudStreamChannels.TO_UPPERCASE_REQUEST)
    public void process(Message<StringWrapper> request) {
      StringWrapper uppercase = null;
      try {
        uppercase = toUppercase(request);
      } catch (MyException e) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(e).setHeader("__TypeId__", e.getClass().getName())
                .copyHeaders(request.getHeaders()).build());
      }
      if (uppercase != null) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(uppercase)
                .setHeader("__TypeId__", StringWrapper.class.getName())
                .copyHeaders(request.getHeaders()).build());
      }
    }

    private StringWrapper toUppercase(Message<StringWrapper> request) throws MyException {
      Random random = new Random();
      int number = random.nextInt(50) + 1;
      if (number > 25) {
        throw new MyException("An error occurred.");
      }
      return new StringWrapper(request.getPayload().getString().toUpperCase());
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(CloudStreamApplication.class, args);
  }

}
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;

@EnableBinding({StreamListenerApplication.CloudStreamChannels.class})
@SpringBootApplication
public class StreamListenerApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

  }

  public static void main(String[] args) {
    SpringApplication.run(StreamListenerApplication.class, args);
  }

  @Autowired
  CloudStreamChannels channels;

  @StreamListener(CloudStreamChannels.TO_UPPERCASE_REPLY)
  public void processToUppercaseReply(Message<StringWrapper> message) {
    System.out.println("Processing message: " + message.getPayload());
  }

}
StringWrapper.java(在所有三个项目中使用)

CloudStreamApplication.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.dsl.HeaderEnricherSpec;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@EnableBinding({GatewayApplication.GatewayChannels.class})
@SpringBootApplication
public class GatewayApplication {

  @Component
  public interface GatewayChannels {
    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Output(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @MessagingGateway
  public interface StreamGateway {
    public static final String ENRICH = "enrich";

    @Gateway(requestChannel = ENRICH, replyChannel = GatewayChannels.TO_UPPERCASE_REPLY)
    StringWrapper process(StringWrapper payload) throws MyException;
  }

  @RestController
  public class UppercaseController {
    @Autowired
    StreamGateway gateway;

    @GetMapping(value = "/string/{string}",
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
    public ResponseEntity<StringWrapper> getUser(@PathVariable("string") String string) {
      try {
        StringWrapper result = gateway.process(new StringWrapper(string));
        // Instead of catching the exception in the below catch clause, here we have just a string
        // representation of the stack trace when an exception occurs.
        return new ResponseEntity<StringWrapper>(result, HttpStatus.OK);
      } catch (MyException e) {
        // Why is the exception not caught here?
        return new ResponseEntity<StringWrapper>(new StringWrapper("An error has occurred"),
            HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
  }

  @Bean
  public IntegrationFlow headerEnricherFlow() {
    return IntegrationFlows.from(StreamGateway.ENRICH)
        .enrichHeaders(HeaderEnricherSpec::headerChannelsToString)
        .channel(GatewayChannels.TO_UPPERCASE_REQUEST).get();
  }

}
package com.example.demo;

import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

@EnableBinding({CloudStreamApplication.CloudStreamChannels.class})
@SpringBootApplication
public class CloudStreamApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Output(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Input(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @Component
  public class Processor {

    @Autowired
    CloudStreamChannels channels;

    @StreamListener(CloudStreamChannels.TO_UPPERCASE_REQUEST)
    public void process(Message<StringWrapper> request) {
      StringWrapper uppercase = null;
      try {
        uppercase = toUppercase(request);
      } catch (MyException e) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(e).setHeader("__TypeId__", e.getClass().getName())
                .copyHeaders(request.getHeaders()).build());
      }
      if (uppercase != null) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(uppercase)
                .setHeader("__TypeId__", StringWrapper.class.getName())
                .copyHeaders(request.getHeaders()).build());
      }
    }

    private StringWrapper toUppercase(Message<StringWrapper> request) throws MyException {
      Random random = new Random();
      int number = random.nextInt(50) + 1;
      if (number > 25) {
        throw new MyException("An error occurred.");
      }
      return new StringWrapper(request.getPayload().getString().toUpperCase());
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(CloudStreamApplication.class, args);
  }

}
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;

@EnableBinding({StreamListenerApplication.CloudStreamChannels.class})
@SpringBootApplication
public class StreamListenerApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

  }

  public static void main(String[] args) {
    SpringApplication.run(StreamListenerApplication.class, args);
  }

  @Autowired
  CloudStreamChannels channels;

  @StreamListener(CloudStreamChannels.TO_UPPERCASE_REPLY)
  public void processToUppercaseReply(Message<StringWrapper> message) {
    System.out.println("Processing message: " + message.getPayload());
  }

}
StreamListenerApplication.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.dsl.HeaderEnricherSpec;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@EnableBinding({GatewayApplication.GatewayChannels.class})
@SpringBootApplication
public class GatewayApplication {

  @Component
  public interface GatewayChannels {
    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Output(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @MessagingGateway
  public interface StreamGateway {
    public static final String ENRICH = "enrich";

    @Gateway(requestChannel = ENRICH, replyChannel = GatewayChannels.TO_UPPERCASE_REPLY)
    StringWrapper process(StringWrapper payload) throws MyException;
  }

  @RestController
  public class UppercaseController {
    @Autowired
    StreamGateway gateway;

    @GetMapping(value = "/string/{string}",
        produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
    public ResponseEntity<StringWrapper> getUser(@PathVariable("string") String string) {
      try {
        StringWrapper result = gateway.process(new StringWrapper(string));
        // Instead of catching the exception in the below catch clause, here we have just a string
        // representation of the stack trace when an exception occurs.
        return new ResponseEntity<StringWrapper>(result, HttpStatus.OK);
      } catch (MyException e) {
        // Why is the exception not caught here?
        return new ResponseEntity<StringWrapper>(new StringWrapper("An error has occurred"),
            HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
  }

  @Bean
  public IntegrationFlow headerEnricherFlow() {
    return IntegrationFlows.from(StreamGateway.ENRICH)
        .enrichHeaders(HeaderEnricherSpec::headerChannelsToString)
        .channel(GatewayChannels.TO_UPPERCASE_REQUEST).get();
  }

}
package com.example.demo;

import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

@EnableBinding({CloudStreamApplication.CloudStreamChannels.class})
@SpringBootApplication
public class CloudStreamApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";
    String TO_UPPERCASE_REQUEST = "to-uppercase-request";

    @Output(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

    @Input(TO_UPPERCASE_REQUEST)
    SubscribableChannel toUppercaseRequest();
  }

  @Component
  public class Processor {

    @Autowired
    CloudStreamChannels channels;

    @StreamListener(CloudStreamChannels.TO_UPPERCASE_REQUEST)
    public void process(Message<StringWrapper> request) {
      StringWrapper uppercase = null;
      try {
        uppercase = toUppercase(request);
      } catch (MyException e) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(e).setHeader("__TypeId__", e.getClass().getName())
                .copyHeaders(request.getHeaders()).build());
      }
      if (uppercase != null) {
        channels.toUppercaseReply()
            .send(MessageBuilder.withPayload(uppercase)
                .setHeader("__TypeId__", StringWrapper.class.getName())
                .copyHeaders(request.getHeaders()).build());
      }
    }

    private StringWrapper toUppercase(Message<StringWrapper> request) throws MyException {
      Random random = new Random();
      int number = random.nextInt(50) + 1;
      if (number > 25) {
        throw new MyException("An error occurred.");
      }
      return new StringWrapper(request.getPayload().getString().toUpperCase());
    }
  }

  public static void main(String[] args) {
    SpringApplication.run(CloudStreamApplication.class, args);
  }

}
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;

@EnableBinding({StreamListenerApplication.CloudStreamChannels.class})
@SpringBootApplication
public class StreamListenerApplication {

  @Component
  interface CloudStreamChannels {

    String TO_UPPERCASE_REPLY = "to-uppercase-reply";

    @Input(TO_UPPERCASE_REPLY)
    SubscribableChannel toUppercaseReply();

  }

  public static void main(String[] args) {
    SpringApplication.run(StreamListenerApplication.class, args);
  }

  @Autowired
  CloudStreamChannels channels;

  @StreamListener(CloudStreamChannels.TO_UPPERCASE_REPLY)
  public void processToUppercaseReply(Message<StringWrapper> message) {
    System.out.println("Processing message: " + message.getPayload());
  }

}

@MessagingGateway
上只有一个全局
错误通道
,用于所有
@Gateway
方法。如果您有一个具有多个
@gateway
方法的网关,则每个方法都可以设置一个消息头来指示失败的方法

如果您向网关的回复通道(并且没有错误通道)发送
消息
,则有效负载将被抛出给调用者

如果网关方法具有
throws
子句,则会尝试展开原因树以查找该异常

如果添加一个
errorChannel
,而不是向调用者抛出异常,一个带有异常的
ErrorMessage
作为其有效负载发送到错误通道-然后可以对错误通道流执行任何进一步的后处理,并在需要时向调用者抛出一些其他异常。不过,听起来你不需要这个

所以,把这一切放在一起

  • 让错误处理服务向另一个目标发送一些消息
  • 在网关服务中,为该目标添加一个
    @StreamListener
  • @StreamListener
    中,构造一条带有
    异常
    负载的消息,并将其发送到网关的应答通道
  • 然后网关将有效负载抛出给调用者
  • 像这样的东西应该有用

    @Gateway(requestChannel = ENRICH, replyChannel = GatewayChannels.TO_UPPERCASE_REPLY)
    String process(String payload) throws MyException;
    

    POM

    http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0

    <groupId>com.example</groupId>
    <artifactId>so47948454a</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>so47948454a</name>
    <description>Demo project for Spring Boot</description>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-java-dsl</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    application.yml:

    spring:
      cloud:
        stream:
          bindings:
            gateIn:
              destination: serviceOut
              content-type: application/json
            gateOut:
              destination: serviceIn
              content-type: application/json
            serviceIn:
              destination: serviceIn
              content-type: application/json
            serviceOut:
              destination: serviceOut
              content-type: application/json
    

    你要么接受阿泰姆对另一个问题的回答(或者至少给他一个投票权,这样如果你决定接受你自己总结最终解决方案的答案,他会得到一些信任)。请看。谢谢您的反馈,Gary。我已经整理了一些示例代码,并将其作为更新包含在我的原始帖子中。这与我试图完成的内容类似,我不确定为什么在GatewayApplication.java中我没有遇到@RestController类的catch块。根据您的回答,我认为这应该是可行的king.你看到我忘记的东西了吗?异常通常不是JSON友好的(例如,no arg-CTOR)。这就是为什么我建议发送一条普通消息并在本地生成异常。您是否在网关服务器上的日志中看到任何错误?如果您在
    MyException
    中添加一个no-arg-CTOR和一个setter,会发生什么情况?使用调试日志同时运行两侧应该会有所帮助。Gary,我刚刚再次更新了代码。这有点不对劲。我正在参考ng字符串而不是StringWrapper。现在我看到以下错误:
    {“timestamp”:1514307185336,“status”:500,“error”:“Internal Server error”,“exception”:“org.springframework.core.convert.ConverterNotFoundException”,“message”:“未找到能够从[java.lang.String]类型转换的转换器”要键入[com.example.demo.StringWrapper],“path”:“/string/hellobuddy”}
    。我不想在这里添加另一个问题,但是您可以运行我的示例代码来查看问题吗?快乐路径有效吗?我不知道网关服务如何获得任何关于将JSON解组到哪个对象的线索。在任何情况下,因为您有两种返回类型(
    StringWrapper
    MyException
    )您将需要更复杂的配置,例如回复通道路径上的转换器和/或路由器。我在回答中的第一个示例就是这样做的(为异常的不同目的地添加了一个流侦听器。只需使用回复通道的名称
    @Autowired
    a
    MessageChannel
    ,并将异常发送给它即可。
    2017-12-26 13:56:18.121  INFO 39008 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: SpringAMQP#7e87ef9e:0/SimpleConnection@45843650 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 60995]
    Foo [foo=bar]
    MyException [error=failed]
    2017-12-26 13:56:18.165  INFO 39008 --- [           main] com.example.So47948454aApplication       : Started So47948454aApplication in 3.422 seconds (JVM running for 3.858)
    
    spring:
      cloud:
        stream:
          bindings:
            gateIn:
              destination: serviceOut
              content-type: application/json
            gateOut:
              destination: serviceIn
              content-type: application/json
            serviceIn:
              destination: serviceIn
              content-type: application/json
            serviceOut:
              destination: serviceOut
              content-type: application/json