O nouă modalitate de utilizare a e-mailului pentru aplicații de asistență: un tutorial AWS
Publicat: 2022-03-11E-mailul poate să nu fie la fel de cool ca alte platforme de comunicare, dar lucrul cu el poate fi totuși distractiv. Recent, am fost însărcinat cu implementarea mesageriei într-o aplicație mobilă. Singura captură a fost că comunicarea reală trebuia să fie prin e-mail. Ne-am dorit ca utilizatorii aplicației să poată comunica cu o echipă de asistență așa cum ați trimite un mesaj text. Membrii echipei de asistență trebuiau să primească aceste mesaje prin e-mail și, de asemenea, trebuiau să poată răspunde utilizatorului de origine. Pentru utilizatorul final, totul trebuie să arate și să funcționeze la fel ca orice altă aplicație modernă de mesagerie.
În acest articol, vom arunca o privire asupra modului de implementare a unui serviciu similar cu cel descris mai sus folosind Java și câteva dintre serviciile web Amazon. Veți avea nevoie de un cont AWS valid, un nume de domeniu și acces la IDE-ul dvs. Java preferat.
Infrastructura
Înainte de a scrie orice cod, vom configura serviciile AWS necesare pentru rutarea și consumul de e-mail. Vom folosi SES pentru trimiterea și consumul de e-mailuri și SNS+SQS pentru rutarea mesajelor primite.
Totul începe aici cu SES. Începeți prin a vă conecta la contul dvs. AWS și navigați la consola SES.
Înainte de a începe, veți avea nevoie de un nume de domeniu verificat de la care puteți trimite e-mailuri.
Acesta va fi de la care utilizatorii aplicației de domeniu vor trimite mesaje de e-mail și la care vor răspunde membrii de asistență. Verificarea unui domeniu cu SES este un proces simplu, iar mai multe informații pot fi găsite aici.
Dacă este prima dată când utilizați SES sau nu ați solicitat o limită de trimitere, contul dvs. va fi închis. Aceasta înseamnă că nu veți putea trimite e-mail la adrese care nu sunt verificate cu AWS. Acest lucru poate cauza o eroare mai târziu în tutorial, când trimitem un e-mail către biroul nostru de asistență fictiv. Pentru a evita acest lucru, puteți verifica orice adresă de e-mail pe care intenționați să o utilizați ca birou de asistență în consola SES din fila Adrese de e-mail.
Odată ce aveți un domeniu verificat, putem crea un set de reguli. Navigați la fila Seturi de reguli din consola SES și creați o nouă regulă de primire.
Primul pas atunci când creați o regulă de primire va fi definirea unui destinatar.
Filtrele pentru destinatari vă vor permite să definiți ce e-mailuri va consuma SES și cum să procesați fiecare mesaj primit. Destinatarul pe care îl definim aici trebuie să se potrivească cu domeniul și modelul de adrese de la care sunt trimise mesajele utilizatorului prin e-mail. Cel mai simplu caz aici ar fi să adăugați un destinatar pentru domeniul pe care l-am verificat anterior, în cazul nostru example.com . Aceasta va configura SES să aplice regula noastră tuturor e-mailurilor trimise către example.com . (de ex. [email protected], [email protected]).
Pentru a crea o regulă pentru întregul nostru domeniu, am adăuga un destinatar de exemplu.com .
De asemenea, este posibil să se potrivească modele de adrese. Acest lucru este util dacă doriți să direcționați mesajele primite către diferite cozi SQS.
Să presupunem că avem coada A și coada B. Am putea adăuga doi destinatari: [email protected] și [email protected] . Dacă dorim să inserăm un mesaj în coada A, vom trimite un e-mail la [email protected]. Partea a din aceasta se va potrivi cu destinatarul nostru [email protected] . Totul dintre + și @ este date arbitrare ale utilizatorului, nu va afecta potrivirea adresei SES. Pentru a introduce în coada B, înlocuiți pur și simplu a cu b.
După ce vă definiți destinatarii, următorul pas este să configurați acțiunea pe care o va efectua SES după consumarea unui nou e-mail. În cele din urmă dorim ca acestea să ajungă în SQS, totuși, în prezent, nu este posibil să trecem direct de la SES la SQS. Pentru a reduce decalajul, trebuie să folosim SNS. Selectați acțiunea SNS și creați un subiect nou. În cele din urmă vom configura acest subiect pentru a insera mesaje în SQS.
Selectați creați subiect SNS și dați-i un nume.
După ce subiectul este creat, trebuie să selectăm o codificare a mesajului. Voi folosi Base64 pentru a păstra caracterele speciale. Codificarea pe care o alegeți va afecta modul în care mesajele sunt decodificate atunci când le consumăm în serviciul nostru.
Odată stabilită regula, trebuie doar să o denumim.
Următorul pas va fi configurarea SQS și SNS, pentru asta trebuie să ne îndreptăm către consola SQS și să creăm o nouă coadă.
Pentru a simplifica lucrurile, folosesc același nume ca subiectul nostru SNS.
După ce ne definim coada, va trebui să-i ajustam politica de acces. Vrem doar să acordăm permisiunea de inserare a subiectului nostru SNS. Putem realiza acest lucru adăugând o condiție care se potrivește cu subiectul nostru SNS.
Câmpul de valoare ar trebui să fie completat cu ARN-ul pentru subiectul SNS pe care SES îl notifică.
După ce SQS este configurat, este timpul pentru încă o călătorie înapoi la consola SNS pentru a configura subiectul pentru a introduce notificări în noua ta coadă SQS strălucitoare.
În consola SNS, selectați subiectul pe care SES îl notifică. De acolo, creați un nou abonament. Protocolul de abonament ar trebui să fie Amazon SQS, iar destinația ar trebui să fie ARN-ul cozii SQS pe care tocmai ați generat-o.
După toate acestea, partea AWS a ecuației ar trebui să fie configurată. Ne putem testa munca prin e-mail. Trimiteți un e-mail către domeniul configurat cu SES, apoi mergeți la consola SQS și selectați coada dvs. Ar trebui să puteți vedea încărcătura utilă care conține e-mailul dvs.
Serviciu Java pentru tratarea e-mailurilor
Acum trecem la partea distractivă! În această secțiune, vom crea un microserviciu simplu capabil să trimită mesaje și să proceseze e-mailurile primite. Primul pas va fi definirea unui API care va trimite prin e-mail biroul nostru de asistență în numele unui utilizator.
O notă rapidă. Ne vom concentra pe componentele logicii de afaceri ale acestui serviciu și nu vom defini punctele finale REST sau un strat de persistență.
Pentru a construi un serviciu Spring, vom folosi Spring Boot și Maven. Putem folosi Spring Initializer pentru a genera un proiect pentru noi, start.spring.io.
Pentru a începe, pom.xml ar trebui să arate cam așa:
<?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>
Trimiterea de asistență prin e-mail în numele unui utilizator
Mai întâi, să definim un bean pentru a trimite e-mail biroului nostru de asistență în numele unui utilizator. Sarcina acestui bean va fi să proceseze un mesaj primit de la un ID de utilizator și să trimită mesajul respectiv la adresa de e-mail predefinită a biroului de asistență.
Să începem prin a defini o interfață.
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); }
Și o implementare goală:
@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 } }
Să adăugăm, de asemenea, AWS SDK la pom-ul nostru, vom folosi clientul SES pentru a ne trimite e-mailurile:
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.5</version> </dependency>
Primul lucru pe care trebuie să-l facem este să generăm o adresă de e-mail de la care să trimitem mesajul utilizatorului nostru. Adresa pe care o generăm va juca un rol critic în partea consumatoare a serviciului nostru. Trebuie să conțină suficiente informații pentru a direcționa răspunsul biroului de asistență înapoi către utilizatorul de origine.
Pentru a realiza acest lucru, vom include ID-ul utilizatorului de origine în adresa noastră de e-mail generată. Pentru a păstra lucrurile curate, vom crea un obiect care conține ID-ul utilizatorului și vom folosi șirul JSON codificat Base64 al acestuia ca adresă de e-mail.
Să creăm un nou bean responsabil pentru transformarea unui ID de utilizator într-o adresă de e-mail.
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); }
Să începem implementarea adăugând consimțământurile necesare și o clasă interioară simplă care ne va ajuta să serializăm 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; } } }
Generarea adresei noastre de e-mail este simplă, tot ce trebuie să facem este să creăm un obiect UserDetails și să codificăm în Base64 reprezentarea JSON. Versiunea finală a metodei createAddressForUserID ar trebui să arate cam așa:
@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; }
Acum ne putem întoarce la SupportBeanSesImpl și îl putem actualiza pentru a folosi noul bean de e-mail pe care tocmai l-am creat.
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); }
Pentru a trimite e-mailuri, vom folosi clientul AWS SES inclus cu AWS SDK.
/** * SES client */ private final AmazonSimpleEmailService amazonSimpleEmailService = new AmazonSimpleEmailServiceClient( new DefaultAWSCredentialsProviderChain() //see http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html );
Utilizăm DefaultAWSCredentialsProviderChain pentru a gestiona acreditările pentru noi, această clasă va căuta acreditările AWS așa cum este definit aici.
Vom merge la o cheie de acces AWS furnizată cu acces la SES și eventual SQS. Pentru mai multe informații, consultați documentația de la Amazon.
Următorul pas va fi actualizarea metodei noastre messageSupport pentru asistența prin e-mail folosind AWS SDK. SDK-ul SES face ca acest proces să fie simplu. Metoda finală ar trebui să arate cam așa:
@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); }
Pentru a o încerca, creați o clasă de testare și injectați SupportBean. Asigurați-vă că SUPPORT_EMAIL_ADDRESS definit în SupportBeanSesImpl indică o adresă de e-mail pe care o dețineți. Dacă contul dvs. SES este în sandbox, această adresă trebuie, de asemenea, verificată. Adresele de e-mail pot fi verificate în consola SES din secțiunea Adrese de e-mail.
@Test public void emailSupport() throws JsonProcessingException { supportBean.messageSupport(1, "Hello World!"); }
După ce rulați acest lucru, ar trebui să vedeți un mesaj afișat în căsuța dvs. de e-mail. Mai bine, răspundeți la mesaj și verificați coada SQS pe care am configurat-o mai devreme. Ar trebui să vedeți o sarcină utilă care conține răspunsul dvs.
Consumând răspunsuri de la SQS
Ultimul pas va fi să citiți e-mailurile de la SQS, să analizați mesajul de e-mail și să aflați ce ID de utilizator îi aparține răspunsul.
Pentru a asculta mesaje noi SQS, vom folosi SDK-ul de mesagerie Spring Cloud AWS. Acest lucru ne va permite să configuram un ascultător de mesaje SQS prin adnotări și, astfel, să evităm destul de mult codul standard.

În primul rând, dependențele necesare.
Adăugați dependența de mesagerie Spring Cloud:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws-messaging</artifactId> </dependency>
Și adăugați Spring Cloud AWS la gestionarea dependenței dvs. de 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>
În prezent, Spring Cloud AWS nu acceptă configurația bazată pe adnotări, așa că va trebui să definim un bean XML. Din fericire, nu avem nevoie de prea multă configurație, așa că definiția noastră va fi destul de ușoară. Principalul punct al acestui fișier va fi activarea ascultătorilor de coadă bazați pe adnotări, acest lucru ne va permite să adnotăm o metodă ca un SqsListener.
Creați un nou fișier XML denumit aws-config.xml în folderul de resurse. Definiția noastră ar trebui să arate cam așa:
<?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>
Partea importantă a acestui fișier este <aws-messaging:annotation-driven-queue-listener />
. De asemenea, definim o regiune implicită. Acest lucru nu este necesar, dar acest lucru ne va permite să facem referire la coada noastră SQS după nume în loc de URL. Nu definim nicio acreditări AWS, omițându-le, Spring va fi implicit DefaultAWSCredentialsProviderChain, același furnizor pe care l-am folosit mai devreme în bean-ul nostru SES. Mai multe informații pot fi găsite în documentele Spring Cloud AWS.
Pentru a folosi această configurație XML în aplicația noastră Spring Boot, trebuie să o importăm în mod explicit. Mergeți la clasa dvs. @SpringBootApplication și importați-o.
@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); } }
Acum să definim un bean care va gestiona mesajele SQS primite. Spring Cloud AWS ne permite să realizăm acest lucru cu o singură adnotare!
/** * 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); } }
Magia aici constă în adnotarea @SqsListener. Prin aceasta, Spring va înființa un Executor și va începe să sondajăm SQS pentru noi. De fiecare dată când este găsit un mesaj nou, metoda noastră adnotată va fi invocată cu conținutul mesajului. Opțional, Spring Cloud poate fi configurat pentru a distribui mesajele primite, oferindu-vă posibilitatea de a lucra cu obiecte puternic tastate în interiorul listei de ascultăre. În plus, aveți posibilitatea de a injecta un singur antet sau o hartă a tuturor antetelor returnate de la apelul AWS subiacent.
Putem folosi aici numele cozii logice, deoarece am definit anterior regiunea în aws-config.xml, dacă dorim să omitem, am putea înlocui valoarea cu URL-ul nostru SQS complet calificat. De asemenea, definim o politică de ștergere, aceasta va configura Spring să șteargă mesajul primit din SQS dacă condiția acestuia este îndeplinită. Există mai multe politici definite în SqsMessageDeletionPolicy, configurăm Spring pentru a ne șterge mesajul dacă metoda noastră consumeSqsMessage se execută cu succes.
De asemenea, injectăm anteturile SQS returnate în metoda noastră folosind @Headers, iar harta injectată va conține metadate legate de coada și sarcina utilă primită. Corpul mesajului este injectat folosind @NotificationMessage. Spring acceptă triajul utilizând Jackson sau printr-un convertor personalizat de corp de mesaj. Din motive de comoditate, doar vom injecta șirul JSON brut și vom lucra cu el folosind clasa JSONObject inclusă cu AWS SDK.
Sarcina utilă preluată din SQS va conține o mulțime de date. Aruncă o privire la JSONObject pentru a te familiariza cu sarcina utilă returnată. Sarcina noastră utilă conține date de la fiecare serviciu AWS prin care a fost transmis, SES, SNS și, în final, SQS. De dragul acestui tutorial, ne pasă doar de două lucruri: lista adreselor de e-mail la care a fost trimisă și corpul e-mailului. Să începem prin a analiza e-mailurile.
//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); }
Deoarece în lumea reală, biroul nostru de asistență poate include mai mult decât expeditorul inițial în răspunsul său, vom dori să verificăm adresa înainte de a analiza ID-ul utilizatorului. Acest lucru va oferi biroului nostru de asistență atât capacitatea de a trimite mesaje mai multor utilizatori în același timp, cât și capacitatea de a include utilizatori care nu sunt aplicații.
Să ne întoarcem la interfața noastră UserEmailBean și să adăugăm o altă metodă.
/** * Returns true if the input email address matches our template * @param emailAddress Email to check * @return true if it matches */ boolean emailMatchesUserFormat(String emailAddress);
În UserEmailBeanJSONImpl, pentru a implementa această metodă, vom dori să facem două lucruri. Mai întâi, verificați dacă adresa se termină cu EMAIL_DOMAIN-ul nostru, apoi verificați dacă o putem distribui.
@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("@")); }
Am definit două metode noi, emailMatchesUserFormat pe care tocmai le-am adăugat la interfața noastră și o metodă simplă de utilitate pentru împărțirea unei adrese de e-mail pe @. Implementarea noastră emailMatchesUserFormat funcționează prin încercarea de a decoda Base64 și de a distribui partea de adresă înapoi în clasa noastră de ajutor UserDetails. Dacă acest lucru reușește, verificăm apoi pentru a ne asigura că este completat ID-ul de utilizator necesar. Dacă toate acestea funcționează, putem presupune cu siguranță o potrivire.
Reveniți la EmailSqsListener și injectați UserEmailBean proaspăt actualizat.
private final UserEmailBean userEmailBean; @Autowired public EmailSqsListener(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; }
Acum vom actualiza consumeSqsMethod. Mai întâi, să analizăm corpul e-mailului:
//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); }
Acum să creăm o nouă metodă care va procesa adresa de e-mail și corpul e-mailului.
private void processEmail(String emailAddress, String emailBody){ }
Și, în sfârșit, actualizați bucla de e-mail pentru a invoca această metodă dacă găsește o potrivire.
//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); } }
Înainte de a implementa processEmail, trebuie să mai adăugăm o metodă la UserEmailBean. Avem nevoie de o metodă pentru returnarea ID-ului de utilizator dintr-un e-mail. Întoarceți-vă la interfața UserEmailBean pentru a adăuga ultima sa metodă.
/** * 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);
Scopul acestei metode va fi returnarea userID-ului de la o adresă formatată. Implementarea va fi similară cu metoda noastră de verificare. Să mergem la UserEmailBeanJSONImpl și să completăm această metodă.
@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; } }
Acum reveniți la EmailSqsListener și actualizați procesulEmail pentru a utiliza această nouă metodă.
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; } }
Grozav! Acum avem aproape tot ce ne trebuie. Ultimul lucru pe care trebuie să-l facem este să analizăm răspunsul din mesajul brut.
Analizarea răspunsurilor de la e-mailuri este de fapt o sarcină destul de complicată. Formatele de mesaje de e-mail nu sunt standardizate, iar variațiile între diferiți clienți de e-mail pot fi uriașe. Răspunsul brut va include, de asemenea, mult mai mult decât răspunsul și o semnătură. Cel mai probabil, mesajul original va fi inclus și. Oamenii inteligenți de la Mailgun au creat o postare grozavă pe blog care explică unele dintre provocări. De asemenea, au deschis abordarea bazată pe învățare automată pentru analizarea e-mailurilor, consultați-o aici.
Biblioteca Mailgun este scrisă în Python, așa că pentru tutorialul nostru vom folosi o soluție mai simplă bazată pe Java. Utilizatorul GitHub edlio a creat un parser de e-mail cu licență MIT în Java, bazat pe una dintre bibliotecile GitHub. Vom folosi această bibliotecă grozavă.
Mai întâi, să ne actualizăm pom-ul, vom folosi https://jitpack.io pentru a trage EmailReplyParser.
<repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>
Acum adăugați dependența GitHub.
<dependency> <groupId>com.github.edlio</groupId> <artifactId>EmailReplyParser</artifactId> <version>v1.0</version> </dependency>
De asemenea, vom folosi e-mailul Apache commons. Va trebui să analizăm e-mailul brut într-un javax.mail MimeMessage înainte de a-l transmite EmailReplyParser. Adăugați dependența de bunuri comune.
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency>
Acum ne putem întoarce la EmailSqsListener și putem finaliza procesul de e-mail. În acest moment, avem ID-ul utilizatorului de origine și corpul e-mailului brut. Singurul lucru care rămâne de făcut este să analizeze răspunsul.
Pentru a realiza acest lucru, vom folosi o combinație de javax.mail și EmailReplyParser de la edlio.
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); }
Învelire
Si asta e! Acum avem tot ce ne trebuie pentru a oferi un răspuns utilizatorului care inițiază!
În acest articol, am văzut cum Amazon Web Services poate fi folosit pentru a orchestra conducte complexe. Deși în acest articol, conducta a fost concepută în jurul e-mailurilor; aceleași instrumente pot fi utilizate pentru a proiecta sisteme și mai complexe, unde nu trebuie să vă faceți griji cu privire la întreținerea infrastructurii și vă puteți concentra pe aspectele distractive ale ingineriei software.