Destek Uygulamaları için E-posta Kullanmanın Yeni Bir Yolu: Bir AWS Eğitimi
Yayınlanan: 2022-03-11E-posta, diğer iletişim platformları kadar havalı olmayabilir, ancak onunla çalışmak yine de eğlenceli olabilir. Kısa süre önce bir mobil uygulamada mesajlaşmayı uygulamakla görevlendirildim. Tek yakalama, gerçek iletişimin e-posta üzerinden olması gerektiğiydi. Uygulama kullanıcılarının, tıpkı bir metin mesajı gönderir gibi bir destek ekibiyle iletişim kurabilmelerini istedik. Destek ekibi üyelerinin bu mesajları e-posta yoluyla almaları ve ayrıca kaynak kullanıcıya yanıt verebilmeleri gerekiyordu. Son kullanıcı için, diğer modern mesajlaşma uygulamaları gibi görünmesi ve çalışması için gereken her şey.
Bu yazıda, Java ve bir avuç Amazon'un web hizmetini kullanarak yukarıda açıklanana benzer bir hizmetin nasıl uygulanacağına bir göz atacağız. Geçerli bir AWS hesabına, bir etki alanı adına ve en sevdiğiniz Java IDE'ye erişime ihtiyacınız olacak.
Altyapı
Herhangi bir kod yazmadan önce, e-posta yönlendirmek ve tüketmek için gerekli AWS hizmetlerini ayarlayacağız. E-posta göndermek ve tüketmek için SES'i ve gelen mesajları yönlendirmek için SNS+SQS'yi kullanacağız.
Her şey burada SES ile başlıyor. AWS hesabınıza giriş yaparak ve SES konsoluna giderek başlayın.
Başlamadan önce, e-posta gönderebileceğiniz doğrulanmış bir alan adına ihtiyacınız olacak.
Bu, kullanıcılarının e-posta mesajları göndereceği ve destek üyelerinin yanıtlayacağı etki alanı uygulaması olacaktır. Bir etki alanını SES ile doğrulamak basit bir işlemdir ve daha fazla bilgiyi burada bulabilirsiniz.
SES'i ilk kez kullanıyorsanız veya gönderme limiti talep etmediyseniz, hesabınız korumalı alana alınır. Bu, AWS ile doğrulanmayan adreslere e-posta gönderemeyeceğiniz anlamına gelir. Bu, öğreticide daha sonra kurgusal yardım masamıza bir e-posta gönderdiğimizde bir hataya neden olabilir. Bunu önlemek için, E-posta Adresleri sekmesindeki SES konsolunda yardım masanız olarak kullanmayı planladığınız e-posta adresini doğrulayabilirsiniz.
Doğrulanmış bir alanınız olduğunda, bir kural seti oluşturabiliriz. SES konsolundaki Kural Kümeleri sekmesine gidin ve yeni bir Fiş Kuralı oluşturun.
Bir makbuz kuralı oluştururken ilk adım, bir alıcı tanımlamak olacaktır.
Alıcı filtreleri, SES'in hangi e-postaları tüketeceğini ve gelen her mesajın nasıl işleneceğini tanımlamanıza olanak tanır. Burada tanımladığımız alıcının, uygulama kullanıcı mesajlarının e-postayla gönderildiği alan ve adres kalıbıyla eşleşmesi gerekir. Buradaki en basit durum, bizim durumumuzda, daha önce doğruladığımız alan adı için bir alıcı eklemek olacaktır example.com . Bu, SES'i kuralımızı example.com'a gönderilen tüm e-postalara uygulayacak şekilde yapılandıracaktır. (örn. [email protected], [email protected]).
Tüm etki alanımız için bir kural oluşturmak üzere example.com için bir alıcı ekleriz.
Adres kalıplarını eşleştirmek de mümkündür. Bu, gelen iletileri farklı SQS kuyruklarına yönlendirmek istiyorsanız kullanışlıdır.
A kuyruğumuz ve B kuyruğumuz olduğunu varsayalım. İki alıcı ekleyebiliriz: [email protected] ve [email protected] . A kuyruğuna bir mesaj eklemek istersek, [email protected] adresine e-posta göndeririz. Bunun bir kısmı [email protected] alıcımızla eşleşecek. + ve @ arasındaki her şey isteğe bağlı kullanıcı verileridir, SES'in adres eşleşmesini etkilemez. B kuyruğuna eklemek için a'yı b ile değiştirmeniz yeterlidir.
Alıcılarınızı tanımladıktan sonraki adım, yeni bir e-posta tükettikten sonra SES'in gerçekleştireceği eylemi yapılandırmaktır. Sonunda bunların SQS'de bitmesini istiyoruz, ancak şu anda doğrudan SES'ten SQS'ye geçmek mümkün değil. Boşluğu kapatmak için SNS kullanmamız gerekiyor. SNS eylemini seçin ve yeni bir konu oluşturun. Sonunda bu konuyu SQS'ye mesaj eklemek için yapılandıracağız.
SNS konusu oluştur'u seçin ve ona bir ad verin.
Konu oluşturulduktan sonra bir mesaj kodlaması seçmemiz gerekiyor. Özel karakterleri korumak için Base64 kullanacağım. Seçtiğiniz kodlama, hizmetimizde onları tükettiğimizde mesajların kodunun nasıl çözüleceğini etkiler.
Kural belirlendikten sonra, sadece onu adlandırmamız gerekiyor.
Bir sonraki adım SQS ve SNS'yi yapılandırmak olacak, bunun için SQS konsoluna gitmemiz ve yeni bir kuyruk oluşturmamız gerekiyor.
İşleri basit tutmak için, SNS başlığımızla aynı adı kullanıyorum.
Kuyruğumuzu tanımladıktan sonra, erişim politikasını ayarlamamız gerekecek. Yalnızca SNS konu başlığımıza ekleme izni vermek istiyoruz. Bunu, SNS konu başlığımızla eşleşen bir koşul ekleyerek başarabiliriz.
Değer alanı, SES'in bildirdiği SNS konusu için ARN ile doldurulmalıdır.
SQS kurulduktan sonra, konunuzu parlak yeni SQS sıranıza bildirimler eklemek için yapılandırmak için SNS konsoluna bir kez daha dönmenin zamanı geldi.
SNS konsolunda, SES'in bildirdiği konuyu seçin. Oradan yeni bir abonelik oluşturun. Abonelik protokolü Amazon SQS olmalı ve hedef, az önce oluşturduğunuz SQS kuyruğunun ARN'si olmalıdır.
Tüm bunlardan sonra, denklemin AWS tarafının tamamı kurulmalıdır. Kendimize e-posta göndererek çalışmalarımızı test edebiliriz. SES ile yapılandırılmış etki alanına bir e-posta gönderin, ardından SQS konsoluna gidin ve sıranızı seçin. E-postanızı içeren yükü görebilmeniz gerekir.
E-postalarla Başa Çıkmak için Java Hizmeti
Şimdi eğlenceli kısma geçelim! Bu bölümde, mesaj gönderebilen ve gelen e-postaları işleyebilen basit bir mikro hizmet oluşturacağız. İlk adım, bir kullanıcı adına destek masamıza e-posta gönderecek bir API tanımlamak olacaktır.
Hızlı bir not. Bu hizmetin iş mantığı bileşenlerine odaklanacağız ve REST uç noktaları veya kalıcılık katmanı tanımlamayacağız.
Bir Spring servisi oluşturmak için Spring Boot ve Maven kullanacağız. Spring Initializer'ı bizim için bir proje oluşturmak için kullanabiliriz, start.spring.io.
Başlamak için, pom.xml dosyamız şöyle görünmelidir:
<?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>
Kullanıcı Adına E-posta Desteği Gönderme
İlk olarak, bir kullanıcı adına destek masamıza e-posta göndermek için bir fasulye tanımlayalım. Bu çekirdeğin işi, bir kullanıcı kimliğinden gelen bir mesajı işlemek ve bu mesajı önceden tanımlanmış destek masası e-posta adresimize e-posta ile göndermek olacaktır.
Bir arayüz tanımlayarak başlayalım.
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); }
Ve boş bir uygulama:
@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 } }
Pom'umuza AWS SDK'yı da ekleyelim, e-postalarımızı göndermek için SES istemcisini kullanacağız:
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.5</version> </dependency>
Yapmamız gereken ilk şey, kullanıcımızın mesajını göndermek için bir e-posta adresi oluşturmak. Ürettiğimiz adres, hizmetimizin tüketen tarafında kritik bir rol oynayacaktır. Yardım masasının yanıtını kaynak kullanıcıya geri yönlendirmek için yeterli bilgiyi içermesi gerekir.
Bunu başarmak için, oluşturulan e-posta adresimize kaynak kullanıcı kimliğini ekleyeceğiz. İşleri temiz tutmak için, kullanıcı kimliğini içeren bir nesne oluşturacağız ve bunun Base64 ile kodlanmış JSON dizesini e-posta adresi olarak kullanacağız.
Bir kullanıcı kimliğini bir e-posta adresine dönüştürmekten sorumlu yeni bir fasulye oluşturalım.
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'umuzu seri hale getirmemize yardımcı olacak gerekli izinleri ve basit bir iç sınıfı ekleyerek uygulamamıza başlayalım.
@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; } } }
E-posta adresimizi oluşturmak basittir, tek yapmamız gereken bir UserDetails nesnesi oluşturmak ve Base64, JSON temsilini kodlamak. createAddressForUserID yöntemimizin bitmiş versiyonu şöyle görünmelidir:
@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; }
Artık SupportBeanSesImpl'e geri dönebilir ve yeni oluşturduğumuz yeni e-posta çekirdeğini kullanacak şekilde güncelleyebiliriz.
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); }
E-posta göndermek için AWS SDK'da bulunan AWS SES istemcisini kullanacağız.
/** * SES client */ private final AmazonSimpleEmailService amazonSimpleEmailService = new AmazonSimpleEmailServiceClient( new DefaultAWSCredentialsProviderChain() //see http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html );
Kimlik bilgilerini bizim için yönetmek için DefaultAWSCredentialsProviderChain kullanıyoruz, bu sınıf burada tanımlandığı gibi AWS kimlik bilgilerini arayacaktır.
SES'e ve nihayetinde SQS'ye erişimi olan bir AWS erişim anahtarına gidiyoruz. Daha fazla bilgi için Amazon'dan gelen belgelere bakın.
Bir sonraki adım, mesajSupport yöntemimizi AWS SDK kullanarak e-posta desteğine güncellemek olacaktır. SES SDK, bunu basit bir süreç haline getirir. Bitmiş yöntem şöyle görünmelidir:
@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); }
Denemek için bir test sınıfı oluşturun ve SupportBean'i enjekte edin. SupportBeanSesImpl'de tanımlanan SUPPORT_EMAIL_ADDRESS'in size ait bir e-posta adresini gösterdiğinden emin olun. SES hesabınız korumalı alan ise bu adresin de doğrulanması gerekir. E-posta adresleri, SES konsolunda E-posta Adresleri bölümü altında doğrulanabilir.
@Test public void emailSupport() throws JsonProcessingException { supportBean.messageSupport(1, "Hello World!"); }
Bunu çalıştırdıktan sonra, gelen kutunuzda bir mesaj görünmelidir. Daha da iyisi, mesajı yanıtlayın ve daha önce oluşturduğumuz SQS sırasını kontrol edin. Cevabınızı içeren bir yük görmelisiniz.
SQS'den Gelen Yanıtları Tüketmek
Son adım, SQS'den gelen e-postaları okumak, e-posta mesajını ayrıştırmak ve yanıtın iletilmesi gereken kullanıcı kimliğine ait olduğunu bulmak olacaktır.
Yeni SQS mesajlarını dinlemek için Spring Cloud AWS mesajlaşma SDK'sını kullanacağız. Bu, ek açıklamalar yoluyla bir SQS mesaj dinleyicisini yapılandırmamıza ve böylece oldukça fazla ortak koddan kaçınmamıza izin verecektir.
İlk olarak, gerekli bağımlılıklar.
Spring Cloud mesajlaşma bağımlılığını ekleyin:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws-messaging</artifactId> </dependency>
Ve pom bağımlılık yönetiminize Spring Cloud AWS'yi ekleyin:

<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>
Şu anda Spring Cloud AWS, açıklama odaklı yapılandırmayı desteklemediğinden, bir XML çekirdeği tanımlamamız gerekecek. Neyse ki çok fazla konfigürasyona ihtiyacımız yok, bu yüzden fasulye tanımımız oldukça hafif olacak. Bu dosyanın ana noktası, açıklama odaklı kuyruk dinleyicilerini etkinleştirmek olacaktır, bu, bir SqsListener olarak bir yönteme açıklama eklememize izin verecektir.
Kaynaklar klasörünüzde aws-config.xml adlı yeni bir XML dosyası oluşturun. Tanımımız şöyle görünmelidir:
<?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>
Bu dosyanın önemli kısmı <aws-messaging:annotation-driven-queue-listener />
. Ayrıca varsayılan bir bölge tanımlıyoruz. Bu gerekli değildir, ancak bunu yapmak SQS sıramıza URL yerine ada göre başvurmamıza izin verecektir. Herhangi bir AWS kimlik bilgisi tanımlamıyoruz, bunları atlayarak Spring, varsayılan olarak SES bean'imizde daha önce kullandığımız sağlayıcı olan DefaultAWSCredentialsProviderChain'e geçecektir. Spring Cloud AWS belgelerinde daha fazla bilgi bulunabilir.
Bu XML yapılandırmasını Spring Boot uygulamamızda kullanmak için açıkça içe aktarmamız gerekiyor. @SpringBootApplication sınıfınıza gidin ve onu içe aktarın.
@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); } }
Şimdi gelen SQS mesajlarını işleyecek bir bean tanımlayalım. Spring Cloud AWS, bunu tek bir açıklama ile gerçekleştirmemizi sağlıyor!
/** * 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); } }
Buradaki sihir, @SqsListener ek açıklamasında yatmaktadır. Bununla, Spring bir Yürütücü kuracak ve bizim için SQS'yi yoklamaya başlayacak. Her yeni mesaj bulunduğunda, açıklamalı yöntemimiz mesaj içerikleriyle birlikte çağrılır. İsteğe bağlı olarak, Spring Cloud, gelen mesajları sıralayacak şekilde yapılandırılabilir ve size kuyruk dinleyicinizde güçlü yazılan nesnelerle çalışma yeteneği verir. Ek olarak, tek bir üst bilgi veya temel alınan AWS çağrısından döndürülen tüm üstbilgilerin haritasını ekleme olanağınız vardır.
Bölgeyi daha önce aws-config.xml'de tanımladığımız için burada mantıksal kuyruk adını kullanabiliriz, eğer değeri tam nitelikli SQS URL'mizle değiştirebileceğimizi atlamak istersek. Ayrıca bir silme politikası tanımlıyoruz, bu, Spring'i koşulu karşılanırsa gelen mesajı SQS'den silecek şekilde yapılandıracaktır. SqsMessageDeletionPolicy'de tanımlanmış birden çok ilke vardır, Spring'i, abuseSqsMessage yöntemimiz başarılı bir şekilde yürütülürse mesajımızı silecek şekilde yapılandırıyoruz.
Ayrıca, @Headers kullanarak yöntemimize döndürülen SQS başlıklarını enjekte ediyoruz ve enjekte edilen harita, alınan kuyruk ve yük ile ilgili meta verileri içerecek. Mesaj gövdesi @NotificationMessage kullanılarak enjekte edilir. Spring, Jackson'ı kullanarak veya özel bir mesaj gövdesi dönüştürücüsü aracılığıyla sıralamayı destekler. Kolaylık sağlamak için, ham JSON dizesini enjekte edeceğiz ve AWS SDK'da bulunan JSONObject sınıfını kullanarak onunla çalışacağız.
SQS'den alınan yük çok fazla veri içerecektir. Döndürülen yükü tanımak için JSONObject'e bir göz atın. Yükümüz, geçtiği her AWS hizmetinden, SES, SNS ve son olarak SQS'den gelen verileri içerir. Bu öğretici uğruna, gerçekten yalnızca iki şeyi önemsiyoruz: bunun gönderildiği e-posta adreslerinin listesi ve e-posta gövdesi. E-postaları ayrıştırarak başlayalım.
//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); }
Gerçek dünyada, yardım masamız yanıtında orijinal gönderenden daha fazlasını içerebileceğinden, kullanıcı kimliğini ayrıştırmadan önce adresi doğrulamak isteyeceğiz. Bu, destek masamıza hem birden fazla kullanıcıya aynı anda mesaj gönderme yeteneği hem de uygulama dışı kullanıcıları dahil etme yeteneği verecektir.
UserEmailBean arabirimimize geri dönelim ve başka bir yöntem ekleyelim.
/** * 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'de bu yöntemi uygulamak için iki şey yapmak isteyeceğiz. Önce, adresin EMAIL_DOMAIN ile bitip bitmediğini kontrol edin, ardından onu sıralayabilir miyiz kontrol edin.
@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("@")); }
Arayüzümüze yeni eklediğimiz emailMatchesUserFormat ve @ üzerinde bir e-posta adresini bölmek için basit bir yardımcı program yöntemi olmak üzere iki yeni yöntem tanımladık. emailMatchesUserFormat uygulamamız, Base64 kodunu çözmeye ve adres bölümünü tekrar UserDetails yardımcı sınıfımıza yerleştirmeye çalışarak çalışır. Bu başarılı olursa, gerekli kullanıcı kimliğinin doldurulduğundan emin olmak için kontrol ederiz. Bütün bunlar işe yararsa, güvenle bir eşleşme varsayabiliriz.
EmailSqsListener'ımıza geri dönün ve yeni güncellenen UserEmailBean'i enjekte edin.
private final UserEmailBean userEmailBean; @Autowired public EmailSqsListener(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; }
Şimdi abuseSqsMethod'u güncelleyeceğiz. İlk önce e-posta gövdesini ayrıştıralım:
//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); }
Şimdi e-posta adresini ve e-posta gövdesini işleyecek yeni bir yöntem oluşturalım.
private void processEmail(String emailAddress, String emailBody){ }
Son olarak, bir eşleşme bulursa bu yöntemi çağırmak için e-posta döngüsünü güncelleyin.
//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'i uygulamadan önce, UserEmailBean'imize bir yöntem daha eklememiz gerekiyor. Kullanıcı kimliğini bir e-postadan döndürmek için bir yönteme ihtiyacımız var. Son yöntemini eklemek için UserEmailBean arayüzüne geri dönün.
/** * 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);
Bu yöntemin amacı, biçimlendirilmiş bir adresten kullanıcı kimliğini döndürmek olacaktır. Uygulama, doğrulama yöntemimize benzer olacaktır. UserEmailBeanJSONImpl'e gidelim ve bu yöntemi dolduralım.
@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; } }
Şimdi EmailSqsListener'ımıza geri dönün ve bu yeni yöntemi kullanmak için processEmail'i güncelleyin.
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; } }
Harika! Şimdi ihtiyacımız olan hemen hemen her şeye sahibiz. Yapmamız gereken son şey, ham mesajdan gelen cevabı ayrıştırmaktır.
E-postalardan gelen yanıtları ayrıştırmak aslında oldukça karmaşık bir iştir. E-posta mesajı biçimleri standartlaştırılmamıştır ve farklı e-posta istemcileri arasındaki farklılıklar çok büyük olabilir. Ham yanıt, yanıt ve imzadan çok daha fazlasını içerecektir. Orijinal mesaj da büyük olasılıkla dahil edilecektir. Mailgun'daki akıllı insanlar, bazı zorlukları açıklayan harika bir blog yazısı hazırladı. Ayrıca, e-postaları ayrıştırmaya yönelik makine öğrenimine dayalı yaklaşımlarını açık kaynaklı hale getirdiler, buradan kontrol edin.
Mailgun kitaplığı Python'da yazılmıştır, bu nedenle öğreticimiz için daha basit bir Java tabanlı çözüm kullanacağız. GitHub kullanıcısı edlio, GitHub kitaplıklarından birine dayalı olarak Java'da MIT lisanslı bir e-posta ayrıştırıcısını bir araya getirdi. Bu harika kütüphaneyi kullanacağız.
İlk önce pompamızı güncelleyelim, EmailReplyParser'ı çekmek için https://jitpack.io kullanacağız.
<repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>
Şimdi GitHub bağımlılığını ekleyin.
<dependency> <groupId>com.github.edlio</groupId> <artifactId>EmailReplyParser</artifactId> <version>v1.0</version> </dependency>
Ayrıca Apache commons e-postasını kullanacağız. EmailReplyParser'a göndermeden önce ham e-postayı bir javax.mail MimeMessage'a ayrıştırmamız gerekecek. Commons bağımlılığını ekleyin.
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency>
Artık EmailSqsListener'ımıza dönebilir ve processEmail'i bitirebiliriz. Bu noktada, kaynak kullanıcı kimliğine ve ham e-posta gövdesine sahibiz. Yapılması gereken tek şey cevabı ayrıştırmak.
Bunu başarmak için javax.mail ve edlio'nun EmailReplyParser kombinasyonunu kullanacağız.
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); }
Sarmak
Ve bu kadar! Artık kaynak kullanıcıya bir yanıt vermek için ihtiyacımız olan her şeye sahibiz!
Bu makalede, karmaşık işlem hatlarını düzenlemek için Amazon Web Hizmetlerinin nasıl kullanılabileceğini gördük. Bu makalede, boru hattı e-postalar etrafında tasarlanmış olsa da; bu aynı araçlar, altyapının bakımı konusunda endişelenmenize gerek olmadığı ve bunun yerine yazılım mühendisliğinin eğlenceli yönlerine odaklanabileceğiniz daha karmaşık sistemler tasarlamak için kullanılabilir.