将电子邮件用于支持应用程序的新方法:AWS 教程

已发表: 2022-03-11

电子邮件可能不像其他通信平台那么酷,但使用它仍然很有趣。 我最近的任务是在移动应用程序中实现消息传递。 唯一的问题是实际的交流需要通过电子邮件进行。 我们希望应用用户能够与支持团队进行交流,就像您发送短信一样。 支持团队成员需要通过电子邮件接收这些消息,并且还需要能够回复原始用户。 对于最终用户来说,一切都需要像任何其他现代消息传递应用程序一样外观和功能。

在本文中,我们将了解如何使用 Java 和一些 Amazon 的 Web 服务来实现与上述服务类似的服务。 您将需要一个有效的 AWS 账户、一个域名以及对您最喜欢的 Java IDE 的访问权限。

基础设施

在我们编写任何代码之前,我们将设置路由和使用电子邮件所需的 AWS 服务。 我们将使用 SES 发送和使用电子邮件,使用 SNS+SQS 路由传入消息。

使用 AWS 以编程方式使用电子邮件

使用 Amazon SES 重振支持应用程序中的电子邮件。
鸣叫

一切从 SES 开始。 首先登录您的 AWS 账户并导航到 SES 控制台。

在我们开始之前,您需要一个经过验证的域名,您可以从中发送电子邮件。

这将是域应用程序用户将发送电子邮件和支持成员将回复的。 使用 SES 验证域是一个简单的过程,可以在此处找到更多信息。

如果这是您第一次使用 SES,或者您没有请求发送限制,您的帐户将被沙盒化。 这意味着您将无法向未经 AWS 验证的地址发送电子邮件。 当我们向虚构的帮助台发送电子邮件时,这可能会导致教程稍后出现错误。 为避免这种情况,您可以在电子邮件地址选项卡的 SES 控制台中验证您计划用作帮助台的任何电子邮件地址。

一旦您拥有经过验证的域,我们就可以创建一个规则集。 导航到 SES 控制台中的规则集选项卡并创建一个新的收据规则。

创建接收规则的第一步是定义接收者。

收件人过滤器将允许您定义 SES 将使用哪些电子邮件,以及如何处理每条传入消息。 我们在此处定义的收件人需要与发送电子邮件的应用程序用户消息的域和地址模式相匹配。 这里最简单的情况是为我们之前验证的域添加一个收件人,在我们的例子中是example.com 。 这将配置 SES 以将我们的规则应用于发送到example.com的所有电子邮件。 (例如 [email protected][email protected])。

要为我们的整个域创建规则,我们将为example.com添加一个收件人。

也可以匹配地址模式。 如果您想将传入消息路由到不同的 SQS 队列,这很有用。

假设我们有队列 A 和队列 B。我们可以添加两个收件人: [email protected][email protected] 。 如果我们想将消息插入队列 A,我们会发送电子邮件至 [email protected]。 其中的 a 部分将匹配我们的[email protected]收件人。 +和@之间的都是任意用户数据,不会影响SES的地址匹配。 要插入队列 B,只需将 a 替换为 b。

定义收件人后,下一步是配置 SES 在使用新电子邮件后将执行的操作。 我们最终希望这些最终出现在 SQS 中,但是目前无法直接从 SES 转到 SQS。 为了弥补差距,我们需要使用 SNS。 选择 SNS 操作并创建一个新主题。 我们最终将配置此主题以将消息插入 SQS。

选择创建 SNS 主题并为其命名。

创建主题后,我们需要选择消息编码。 我将使用 Base64 来保留特殊字符。 当我们在服务中使用消息时,您选择的编码将影响消息的解码方式。

一旦设置了规则,我们只需要命名它。

下一步将配置 SQS 和 SNS,为此我们需要前往 SQS 控制台并创建一个新队列。

为简单起见,我使用与我们的 SNS 主题相同的名称。

在我们定义了我们的队列之后,我们需要调整它的访问策略。 我们只想授予我们的 SNS 主题插入权限。 我们可以通过添加与我们的 SNS 主题 arn 匹配的条件来实现这一点。

值字段应填充 SES 正在通知的 SNS 主题的 ARN。

设置 SQS 后,是时候再次返回 SNS 控制台以配置您的主题以将通知插入到您闪亮的新 SQS 队列中。

在 SNS 控制台中,选择 SES 正在通知的主题。 从那里,创建一个新订阅。 订阅协议应该是 Amazon SQS,目标应该是您刚刚生成的 SQS 队列的 ARN。

毕竟,等式的 AWS 方面应该全部设置好。 我们可以通过给自己发电子邮件来测试我们的工作。 向配置了 SES 的域发送一封电子邮件,然后前往 SQS 控制台并选择您的队列。 您应该能够看到包含您的电子邮件的有效负载。

处理电子邮件的 Java 服务

现在进入有趣的部分! 在本节中,我们将创建一个能够发送消息和处理传入电子邮件的简单微服务。 第一步将定义一个 API,它将代表用户向我们的支持台发送电子邮件。

速记。 我们将专注于该服务的业务逻辑组件,不会定义 REST 端点或持久层。

要构建 Spring 服务,我们将使用 Spring Boot 和 Maven。 我们可以使用 Spring Initializer 为我们生成一个项目,start.spring.io。

首先,我们的 pom.xml 应该如下所示:

 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.toptal.tutorials</groupId> <artifactId>email-processor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>email-processor</name> <description>A simple "micro-service" for emailing support on behalf of a user and processing replies</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

代表用户发送电子邮件支持

首先,让我们定义一个 bean,用于代表用户向我们的支持台发送电子邮件。 此 bean 的工作是处理来自用户 ID 的传入消息,并将该消息通过电子邮件发送到我们预定义的支持台电子邮件地址。

让我们从定义一个接口开始。

 public interface SupportBean { /** * Send a message to the application support desk on behalf of a user * @param fromUserId The ID of the originating user * @param message The message to send */ void messageSupport(long fromUserId, String message); }

和一个空的实现:

 @Component public class SupportBeanSesImpl implements SupportBean { /** * Email address for our application help-desk * This is the destination address user support emails will be sent to */ private static final String SUPPORT_EMAIL_ADDRESS = "[email protected]"; @Override public void messageSupport(long fromUserId, String message) { //todo: send an email to our support address } }

让我们也将 AWS SDK 添加到我们的 pom 中,我们将使用 SES 客户端发送我们的电子邮件:

 <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.5</version> </dependency>

我们需要做的第一件事是生成一个电子邮件地址来发送我们用户的消息。 我们生成的地址将在我们服务的消费端发挥关键作用。 它需要包含足够的信息以将帮助台的回复路由回原始用户。

为此,我们将在生成的电子邮件地址中包含原始用户 ID。 为了保持简洁,我们将创建一个包含用户 ID 的对象,并使用它的 Base64 编码 JSON 字符串作为电子邮件地址。

让我们创建一个负责将用户 ID 转换为电子邮件地址的新 bean。

 public interface UserEmailBean { /** * Returns a unique per user email address * @param userID Input user ID * @return An email address unique for the input userID */ String emailAddressForUserID(long userID); }

让我们通过添加所需的同意和一个简单的内部类来开始我们的实现,这将帮助我们序列化我们的 JSON。

 @Component public class UserEmailBeanJSONImpl implements UserEmailBean { /** * The TLD for all our generated email addresses */ private static final String EMAIL_DOMAIN = "example.com"; /** * com.fasterxml.jackson.databind.ObjectMapper used to create a JSON object including our user ID */ private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String emailAddressForUserID(long userID) { //todo: create the email address return null; } /** * Simple helper class we will serialize. * The JSON representation of this class will become our user email address */ private static class UserDetails{ private Long userID; public Long getUserID() { return userID; } public void setUserID(Long userID) { this.userID = userID; } } }

生成我们的电子邮件地址很简单,我们需要做的就是创建一个 UserDetails 对象并对 JSON 表示进行 Base64 编码。 我们的 createAddressForUserID 方法的完成版本应该如下所示:

 @Override public String emailAddressForUserID(long userID) { UserDetails userDetails = new UserDetails(); userDetails.setUserID(userID); //create a JSON representation. String jsonString = objectMapper.writeValueAsString(userDetails); //Base64 encode it String base64String = Base64.getEncoder().encodeToString(jsonString.getBytes()); //create an email address out of it String emailAddress = base64String + "@" + EMAIL_DOMAIN; return emailAddress; }

现在我们可以返回 SupportBeanSesImpl 并更新它以使用我们刚刚创建的新电子邮件 bean。

 private final UserEmailBean userEmailBean; @Autowired public SupportBeanSesImpl(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; } @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //user specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); }

要发送电子邮件,我们将使用 AWS 开发工具包中包含的 AWS SES 客户端。

 /** * SES client */ private final AmazonSimpleEmailService amazonSimpleEmailService = new AmazonSimpleEmailServiceClient( new DefaultAWSCredentialsProviderChain() //see http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html );

我们正在使用 DefaultAWSCredentialsProviderChain 为我们管理凭证,此类将搜索此处定义的 AWS 凭证。

我们将使用预置的 AWS 访问密钥访问 SES 并最终访问 SQS。 有关更多信息,请查看来自 Amazon 的文档。

下一步将使用 AWS 开发工具包将我们的messageSupport方法更新为电子邮件支持。 SES SDK 使这成为一个简单的过程。 完成的方法应如下所示:

 @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //User specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); //create the email Message supportMessage = new Message( new Content("New support request from userID " + fromUserId), //Email subject new Body().withText(new Content(message)) //Email body, this contains the user's message ); //create the send request SendEmailRequest supportEmailRequest = new SendEmailRequest( fromEmail, //From address, our user's generated email new Destination(Collections.singletonList(SUPPORT_EMAIL_ADDRESS)), //to address, our support email address supportMessage //Email body defined above ); //Send it off amazonSimpleEmailService.sendEmail(supportEmailRequest); }

要试用它,请创建一个测试类并注入 SupportBean。 确保 SupportBeanSesImpl 中定义的 SUPPORT_EMAIL_ADDRESS 指向您拥有的电子邮件地址。 如果您的 SES 帐户被沙盒化,则此地址也需要验证。 可以在电子邮件地址部分下的 SES 控制台中验证电子邮件地址。

 @Test public void emailSupport() throws JsonProcessingException { supportBean.messageSupport(1, "Hello World!"); }

运行此程序后,您应该会在收件箱中看到一条消息。 更好的是,回复消息并检查我们之前设置的 SQS 队列。 您应该会看到一个包含您的回复的有效负载。

使用 SQS 的回复

最后一步是读取来自 SQS 的电子邮件,解析电子邮件消息,并确定应转发的回复属于哪个用户 ID。

Amazon SQS 等消息队列服务在面向服务的架构中发挥着至关重要的作用,它允许服务相互通信,而不必牺牲速度、可靠性或可扩展性。

为了侦听新的 SQS 消息,我们将使用 Spring Cloud AWS 消息传递 SDK。 这将允许我们通过注释配置 SQS 消息侦听器,从而避免大量样板代码。

首先,所需的依赖项。

添加 Spring Cloud 消息传递依赖项:

 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws-messaging</artifactId> </dependency>

并将 Spring Cloud AWS 添加到您的 pom 依赖管理中:

 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws</artifactId> <version>1.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

目前,Spring Cloud AWS 不支持注解驱动配置,所以我们将不得不定义一个 XML bean。 幸运的是,我们根本不需要太多配置,所以我们的 bean 定义会很轻松。 该文件的主要目的是启用注释驱动的队列侦听器,这将允许我们将方法注释为 SqsListener。

在资源文件夹中创建一个名为 aws-config.xml 的新 XML 文件。 我们的定义应该是这样的:

 <?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:aws-context="http://www.springframework.org/schema/cloud/aws/context" xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging.xsd"> <!--enable annotation driven queue listeners --> <aws-messaging:annotation-driven-queue-listener /> <!--define our region, this lets us reference queues by name instead of by URL. --> <aws-context:context-region region="us-east-1" /> </beans>

该文件的重要部分是<aws-messaging:annotation-driven-queue-listener /> 。 我们还定义了一个默认区域。 这不是必需的,但这样做将允许我们通过名称而不是 URL 来引用我们的 SQS 队列。 我们没有定义任何 AWS 凭证,通过省略它们,Spring 将默认为 DefaultAWSCredentialsProviderChain,这与我们之前在 SES bean 中使用的提供者相同。 更多信息可以在 Spring Cloud AWS 文档中找到。

要在我们的 Spring Boot 应用程序中使用这个 XML 配置,我们需要显式地导入它。 转到您的 @SpringBootApplication 类并导入它。

 @SpringBootApplication @ImportResource("classpath:aws-config.xml") //Explicit import for our AWS XML bean definition public class EmailProcessorApplication { public static void main(String[] args) { SpringApplication.run(EmailProcessorApplication.class, args); } }

现在让我们定义一个处理传入 SQS 消息的 bean。 Spring Cloud AWS 让我们通过一个注解来完成这个任务!

 /** * Bean reasonable for polling SQS and processing new emails */ @Component public class EmailSqsListener { @SuppressWarnings("unused") //IntelliJ isn't quite smart enough to recognize methods marked with @SqsListener yet @SqsListener(value = "com-example-ses", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS) //Mark this method as a SQS listener //Since we already set up our region we can use the logical queue name here //Spring will automatically delete messages if this method executes successfully public void consumeSqsMessage(@Headers Map<String, String> headers, //Map of headers returned when requesting a message from SQS //This map will include things like the relieved time, count and message ID @NotificationMessage String rawJsonMessage //JSON string representation of our payload //Spring Cloud AWS supports marshaling here as well //For the sake of simplicity we will work with incoming messages as a JSON object ) throws Exception{ //com.amazonaws.util.json.JSONObject included with the AWS SDK JSONObject jsonSqsMessage = new JSONObject(rawJsonMessage); } }

这里的魔力在于@SqsListener 注释。 有了这个,Spring 将设置一个 Executor 并开始为我们轮询 SQS。 每次找到新消息时,都会使用消息内容调用我们的注解方法。 或者,可以将 Spring Cloud 配置为编组传入消息,使您能够在队列侦听器中使用强类型对象。 此外,您可以注入单个标头或从底层 AWS 调用返回的所有标头的映射。

我们可以在此处使用逻辑队列名称,因为我们之前在 aws-config.xml 中定义了该区域,如果我们想省略我们将能够用我们完全限定的 SQS URL 替换该值。 我们还定义了一个删除策略,这将配置 Spring 在满足条件时删除来自 SQS 的传入消息。 SqsMessageDeletionPolicy 中定义了多个策略,如果我们的 consumeSqsMessage 方法成功执行,我们正在配置 Spring 以删除我们的消息。

我们还使用 @Headers 将返回的 SQS 标头注入到我们的方法中,注入的映射将包含与接收到的队列和有效负载相关的元数据。 消息体是使用@NotificationMessage 注入的。 Spring 支持使用 Jackson 或通过自定义消息正文转换器进行编组。 为方便起见,我们将注入原始 JSON 字符串并使用 AWS 开发工具包中包含的 JSONObject 类对其进行处理。

从 SQS 检索到的有效负载将包含大量数据。 查看 JSONObject 以熟悉返回的有效负载。 我们的有效负载包含来自它通过的每个 AWS 服务、SES、SNS 以及最后是 SQS 的数据。 就本教程而言,我们实际上只关心两件事:发送到的电子邮件地址列表和电子邮件正文。 让我们从解析电子邮件开始。

 //Pull out the array containing all email addresses this was sent to JSONArray emailAddressArray = jsonSqsMessage.getJSONObject("mail").getJSONArray("destination"); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

由于在现实世界中,我们的帮助台在其回复中可能不仅仅包含原始发件人,因此我们需要在解析用户 ID 之前验证地址。 这将使我们的支持台能够同​​时向多个用户发送消息,以及包括非应用用户的能力。

让我们回到我们的 UserEmailBean 接口并添加另一个方法。

 /** * Returns true if the input email address matches our template * @param emailAddress Email to check * @return true if it matches */ boolean emailMatchesUserFormat(String emailAddress);

在 UserEmailBeanJSONImpl 中,为了实现这个方法,我们要做两件事。 首先,检查地址是否以我们的 EMAIL_DOMAIN 结尾,然后检查我们是否可以编组它。

 @Override public boolean emailMatchesUserFormat(String emailAddress) { //not our address, return right away if(!emailAddress.endsWith("@" + EMAIL_DOMAIN)){ return false; } //We just care about the email part, not the domain part String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); //We assume this email matches if the address is successfully decoded and marshaled return userDetails != null && userDetails.getUserID() != null; } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class return false; } } /** * Splits an email address on @ * Returns everything before the @ * @param emailAddress Address to split * @return all parts before @. If no @ is found, the entire address will be returned */ private static String splitEmail(String emailAddress){ if(!emailAddress.contains("@")){ return emailAddress; } return emailAddress.substring(0, emailAddress.indexOf("@")); }

我们定义了两个新方法,刚刚添加到界面中的 emailMatchesUserFormat,以及用于在 @ 上拆分电子邮件地址的简单实用方法。 我们的 emailMatchesUserFormat 实现通过尝试 Base64 解码并将地址部分编组回我们的 UserDetails 帮助器类来工作。 如果成功,我们将检查以确保填充了所需的用户 ID。 如果一切顺利,我们可以放心地假设匹配。

回到我们的 EmailSqsListener 并注入新更新的 UserEmailBean。

 private final UserEmailBean userEmailBean; @Autowired public EmailSqsListener(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; }

现在我们要更新consumeSqsMethod。 首先让我们解析出电子邮件正文:

 //Pull our content, remember the content will be Base64 encoded as per our SES settings String encodedContent = jsonSqsMessage.getString("content"); //Create a new String after decoding our body String decodedBody = new String( Base64.getDecoder().decode(encodedContent.getBytes()) ); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

现在让我们创建一个新方法来处理电子邮件地址和电子邮件正文。

 private void processEmail(String emailAddress, String emailBody){ }

最后,更新电子邮件循环以在找到匹配项时调用此方法。

 //Loop over all sent to addresses for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); //If we find a match, process the email and method if(userEmailBean.emailMatchesUserFormat(emailAddress)){ processEmail(emailAddress, decodedBody); } }

在我们实现 processEmail 之前,我们需要向我们的 UserEmailBean 添加一个方法。 我们需要一种从电子邮件中返回用户 ID 的方法。 返回到 UserEmailBean 接口以添加其最后一个方法。

 /** * Returns the userID from a formatted email address. * Returns null if no userID is found. * @param emailAddress Formatted email address, this address should be verified using {@link #emailMatchesUserFormat(String)} * @return The originating userID if found, null if not */ Long userIDFromEmail(String emailAddress);

此方法的目标是从格式化的地址返回用户 ID。 实现将类似于我们的验证方法。 让我们转到 UserEmailBeanJSONImpl 并填写此方法。

 @Override public Long userIDFromEmail(String emailAddress) { String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); if(userDetails == null || userDetails.getUserID() == null){ //We couldn't find a userID return null; } //ID found, return it return userDetails.getUserID(); } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class //Return null since we didn't find a userID return null; } }

现在回到我们的 EmailSqsListener 并更新 processEmail 以使用这个新方法。

 private void processEmail(String emailAddress, String emailBody){ //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } }

伟大的! 现在我们几乎拥有了我们需要的一切。 我们需要做的最后一件事是从原始消息中解析出回复。

电子邮件客户端,就像几年前的网络浏览器一样,受到其实现不一致的困扰。

解析电子邮件中的回复实际上是一项相当复杂的任务。 电子邮件消息格式未标准化,不同电子邮件客户端之间的差异可能很大。 原始响应也将包括比回复和签名更多的内容。 原始消息很可能也会包含在内。 Mailgun 的聪明人整理了一篇很棒的博客文章,解释了一些挑战。 他们还开源了基于机器学习的电子邮件解析方法,在这里查看。

Mailgun 库是用 Python 编写的,因此在我们的教程中,我们将使用更简单的基于 Java 的解决方案。 GitHub 用户 edlio 基于 GitHub 的一个库,用 Java 编写了一个 MIT 许可的电子邮件解析器。 我们将使用这个很棒的库。

首先让我们更新我们的 pom,我们将使用 https://jitpack.io 来拉入 EmailReplyParser。

 <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>

现在添加 GitHub 依赖项。

 <dependency> <groupId>com.github.edlio</groupId> <artifactId>EmailReplyParser</artifactId> <version>v1.0</version> </dependency>

我们还将使用 Apache 公共电子邮件。 在将原始电子邮件传递给 EmailReplyParser 之前,我们需要将原始电子邮件解析为 javax.mail MimeMessage。 添加公共依赖项。

 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency>

现在我们可以回到我们的 EmailSqsListener 并完成 processEmail。 此时,我们有了原始用户 ID 和原始电子邮件正文。 剩下要做的就是解析出回复。

为此,我们将结合使用 javax.mail 和 edlio 的 EmailReplyParser。

 private void processEmail(String emailAddress, String emailBody) throws Exception { //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } //Default javax.mail session Session session = Session.getDefaultInstance(new Properties()); //Create a new mimeMessage out of the raw email body MimeMessage mimeMessage = MimeMessageUtils.createMimeMessage( session, emailBody ); MimeMessageParser mimeMessageParser = new MimeMessageParser(mimeMessage); //Parse the message mimeMessageParser.parse(); //Parse out the reply for our message String replyText = EmailReplyParser.parseReply(mimeMessageParser.getPlainContent()); //Now we're done! //We have both the userID and the response! System.out.println("Processed reply for userID: " + userID + ". Reply: " + replyText); }

包起来

就是这样! 我们现在拥有向原始用户提供响应所需的一切!

看? 我告诉过你电子邮件可以很有趣!

在本文中,我们了解了如何使用 Amazon Web Services 来编排复杂的管道。 尽管在本文中,管道是围绕电子邮件设计的; 这些相同的工具可用于设计更复杂的系统,您不必担心维护基础设施,而可以专注于软件工程的有趣方面。

相关:使用 Amazon Web Services 提高您的工作效率