Java(SpringCloud) 使用Thymeleaf渲染模板,通過Mailgun傳送郵件
好久沒發部落格了,忙東忙西的,堅持!
本文介紹Java使用Mailgun搭建傳送郵件的服務。
我把這個郵件服務放在了我的springCloudApplication(微服務Demo)專案的utilservice服務模組中。
原始碼地址:springCloudApplication
- jdk1.8
- SpringBoot版本:2.0.4.RELEASE
- SpringCloud版本:Finchley.RELEASE
- Intellij IDEA :2018-2
本文涉及到基礎的SpringCloud 知識,對應到本專案中的服務名來說就是:服務註冊中心(eurekaserver),配置中心(configserver),路由(routeservice),負載均衡服務呼叫(uaaservice)。
一、Mailgun
其實程式碼沒多少,就是配置Mailgun需要花費些時間。
先用郵箱註冊好。
Settings,顯示的就是你的賬戶資訊。
Security,這裡的 Private API Key 就是等會要用到的身份認證key。
Authorized Recipients
Upgrade,升級賬戶,貌似是$1。繫結你的信用卡。
接下來跳轉到:https://app.mailgun.com/app/domains
domains,域名。上面個是我自己的域名(這裡本該解析個二級域名的,我直接用頂級域名搞了一次),下面個是當你註冊賬戶時Mailgun為你自動生成的一個二級域名。
剛開始進這個頁面是隻有下面個域名,自己的域名要通過Add New Aomain新增。
新增完之後你的域名會顯示:Unverified,點選自己的域名驗證,往下拉,看Domain Verification & DNS部分
要去自己的域名服務商控制檯配置,我這裡全都配置好了所以全綠。我的域名阿里雲買的,解析記錄如下:
看著這個GIF跟著配置就好了:https://help.mailgun.com/hc/en-us/articles/202052074-How-do-I-verify-my-domain-
我還添加了個SMTP Credentials:
好,現在Mailgun配置算是完成了。接下來上程式碼。還有啥問題就翻它的文件吧。
二、程式碼
我這裡寫了兩種方式的郵件內容,一種純文字內容的,一種thymeleaf模板引擎渲染的html內容的。就放在一起寫了。只上核心程式碼和配置。詳細瞭解看原始碼,文章開頭已給出地址。
專案結構:
- eurekaserver,服務註冊中心
- confiserver,配置中心,幾乎所有功能模組的配置都放在這裡的
- routeserver,路由閘道器
- utilservice,郵件服務所在模組
- uaaservice,在這裡呼叫郵件服務
呼叫服務時傳入收件人郵箱List,郵件的主題,郵件的thymeleaf模板,模板的引數(Map<String,Object>)。然後在郵件服務中渲染模板,然後通過mailgun傳送郵件。
mailgun配置屬性:
mailgun:
domain: mistra.wang
apiKey: key-e9861fdc2c1a6f189528c9cf18c5dc1a
from: 丶小王瑞
fromAddress: [email protected]
mailgunResource: https://api.mailgun.net/v3/mistra.wang/messages
注意:如果寫程式碼的時候把這個配置,就是你的mailgun的敏感資訊,如key等。push到了Github的公共倉庫的話你的郵件服務用不了,你的賬戶會被禁用。並且你會收到郵件提示,如何解決。記得測試完了程式碼再push。還挺嚴謹安全的,我測試完,剛push,馬上我的賬戶就用不了了。
配置屬性安全對映實體類
@Data
@Configuration
@ConfigurationProperties(prefix = "mailgun")
public class MailgunConfigProperties {
private String domain;
private String apiKey;
private String from;
private String fromAddress;
private String mailgunResource;
}
MailgunConfig配置
@Configuration
public class MailgunConfig {
/**
* mailgun配置引數
*/
@Autowired
private MailgunConfigProperties mailgunConfigProperties;
@Bean
public net.sargue.mailgun.Configuration mailgunConfiguration(){
net.sargue.mailgun.Configuration configuration = new net.sargue.mailgun.Configuration()
.from(mailgunConfigProperties.getFrom(), mailgunConfigProperties.getFromAddress())
.apiKey(mailgunConfigProperties.getApiKey())
.domain(mailgunConfigProperties.getDomain());
return configuration;
}
}
thymeleaf模板引擎配置
@Configuration
public class SpringTemplateEngineConfig {
@Bean(name = "stringTemplateEngine")
public SpringTemplateEngine springTemplateEngine() {
//字串模板引擎物件
SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
//內建方言
IDialect springStandardDialect = new SpringStandardDialect();
springTemplateEngine.setDialect(springStandardDialect);
//字串解析器
StringTemplateResolver stringTemplateResolver = new StringTemplateResolver();
//使用快取
stringTemplateResolver.setCacheable(true);
stringTemplateResolver.setTemplateMode(TemplateMode.HTML);
springTemplateEngine.setTemplateResolver(stringTemplateResolver);
return springTemplateEngine;
}
}
執行緒池Bean
@Component
public class ThreadPoolConfig extends ThreadPoolTaskExecutor {
}
郵件實體
@Data
public class MailDTO {
/**
* 傳送物件
*/
@NotEmpty
private List<String> sendToAddress;
/**
* 主題
*/
@NotNull
private String subject;
/**
* 模板
*/
@NotNull
private String template;
/**
* 模板引數
*/
@NotNull
private Map<String,Object> paramsMap;
}
service
package com.mistra.utilservice.service.impl;
import com.mistra.base.result.Result;
import com.mistra.utilservice.config.MailgunConfigProperties;
import com.mistra.utilservice.dto.MailDTO;
import com.mistra.utilservice.service.MailService;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import net.sargue.mailgun.*;
import net.sargue.mailgun.content.Body;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import javax.ws.rs.core.MediaType;
import java.util.regex.Pattern;
/**
* @Author: WangRui
* @Date: 2018/10/14
* Time: 下午10:35
* Description:
*/
@Service
public class MailServiceImpl implements MailService {
private Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);
/**
* 郵箱驗證正則
*/
public static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
/**
* 字串模板解析引擎
*/
@Qualifier("stringTemplateEngine")
@Autowired
private SpringTemplateEngine springTemplateEngine;
@Autowired
private Configuration mailgunConfiguration;
@Autowired
private MailgunConfigProperties mailgunConfigProperties;
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Override
public Result sendMail(MailDTO mailDTO) {
return sendTwo(mailDTO);
}
public Result sendOne(MailDTO mailDTO) {
Result result = new Result();
if (mailDTO.getSendToAddress().size() == 0 || mailDTO.getParamsMap().size() == 0 || StringUtils.isEmpty(mailDTO.getSubject()) || StringUtils.isEmpty(mailDTO.getTemplate())) {
result.setSuccess(false);
result.setMessage("MailDTO引數不正確!");
return result;
}
for (String to : mailDTO.getSendToAddress()) {
if (!Pattern.matches(REGEX_EMAIL, to)) {
result.setSuccess(false);
result.setMessage("郵箱格式錯誤!");
return result;
}
}
//編譯thymeleaf模板 渲染資料
Context context = new Context();
context.setVariables(mailDTO.getParamsMap());
String mailContent = springTemplateEngine.process(mailDTO.getTemplate(), context);
Body mailBody = new Body(mailContent, "");
MailBuilder mailBuilder = Mail.using(mailgunConfiguration);
mailBuilder.subject(mailDTO.getSubject());
mailBuilder.content(mailBody);
for (String sendTo : mailDTO.getSendToAddress()) {
mailBuilder.to(sendTo);
}
result.setSuccess(false);
result.setMessage("郵件傳送成功!");
threadPoolTaskExecutor.submit(() -> {
Response response = mailBuilder.build().send();
logger.info("Send mail complete. Code: {}, Response Type: {}. Message: {}", response.responseCode(), response.responseType(), response.responseMessage());
if (response.responseCode() != 200) {
result.setSuccess(false);
result.setMessage("郵件傳送失敗!" + response.responseMessage());
}
});
return result;
}
/**
* 第二種 傳送純文字郵件
*
* @param mailDTO
* @return
*/
public Result sendTwo(MailDTO mailDTO) {
Result result = new Result();
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api", mailgunConfigProperties.getApiKey()));
WebResource webResource = client.resource(mailgunConfigProperties.getMailgunResource());
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", mailgunConfigProperties.getFrom() + "" + mailgunConfigProperties.getFromAddress());
mailDTO.getSendToAddress().stream().forEach(temp -> formData.add("to", temp));
formData.add("subject", mailDTO.getSubject());
formData.add("text", "純文字郵件測試!");
webResource.type(MediaType.APPLICATION_FORM_URLENCODED).post(ClientResponse.class, formData);
return result;
}
}
三、檢視Mailgun日誌
你可以看到每一次傳送郵件的日誌,報錯原因。
這個錯誤就是我的賬戶被禁用了,暴露了賬戶憑證,就是我把我的賬戶資訊push到了Github公共倉庫引起的。覆蓋提交Commit是不管用的了,它提示你刪庫0-0,麻蛋。
"reject": {
"reason": "exposed account credentials",
"description": ""
},
全文完。另外我是馬自達粉。嘿嘿。