Spring Boot send mail function

What is SMTP?

The full name of SMTP is simple mail transfer protocol (Simple Mail Transfer Protocol). It is a set of specifications used to transfer mail from source address to destination address. It controls the transfer mode of mail. SMTP authentication requires an account and password to log in to the server. Its design is to prevent users from being invaded by spam.

What is IMAP?

The full name of IMAP is Internet Message Access Protocol (Internet Mail Access Protocol). IMAP allows you to obtain mail information and download mail from the mail server. Like POP, IMAP is a mail acquisition protocol.

What is POP3?

The full name of POP3 is Post Office Protocol 3 (post office protocol). POP3 supports client-side remote management of server-side mail. POP3 is often used for "offline" mail processing, that is, the client is allowed to download the server mail, and then the mail on the server will be deleted. At present, many POP3 mail servers only provide the function of downloading mail, and the server itself does not delete mail. This is an improved version of POP3 protocol.

What are the differences between IMAP and POP3 protocols?

The biggest difference between the two is that IMAP allows two-way communication, that is, the operations on the client will be fed back to the server. For example, the client will receive mail, mark read and other operations, and the server will synchronize these operations. Although the POP protocol also allows the client to download server mail, the operations on the client will not be synchronized to the server. For example, when the client receives or marks read mail, the server will not synchronize these operations.

What are JavaMailSender and JavaMailSender impl?

JavaMailSender and JavaMailSenderImpl are the interfaces and implementation classes of integrated mail services officially provided by Spring. They are famous for their simple and efficient design. At present, they are the mainstream tools for sending mail and integrating mail services at the Java back end.

How to send mail through JavaMailSenderImpl?

It is very simple to directly inject JavaMailSenderImpl into the business class and call the send method to send mail. Simple messages can be sent through SimpleMailMessage, while complex messages (such as adding attachments) can be sent through MimeMessageHelper. For example:

@Autowired
private JavaMailSenderImpl mailSender;

public void sendMail() throws MessagingException {

    SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
    simpleMailMessage.setFrom("admin@163.com");
    simpleMailMessage.setTo("socks@qq.com");
    simpleMailMessage.setSubject("Happy New Year");
    simpleMailMessage.setText("Happy New Year!");
    mailSender.send(simpleMailMessage);

    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
    messageHelper.setFrom("admin@163.com");
    messageHelper.setTo("socks@qq.com");
    messageHelper.setSubject("Happy New Year");
    messageHelper.setText("Happy New Year!");
    messageHelper.addInline("doge.gif", new File("xx/xx/doge.gif"));
    messageHelper.addAttachment("work.docx", new File("xx/xx/work.docx"));
    mailSender.send(mimeMessage);
}

Why can JavaMailSenderImpl be used out of the box?

The so-called out of the box is actually based on the official built-in automatic configuration. You can know the mailsenderpropertiesconfiguration class by looking at the source code   A mail service instance (JavaMailSenderImpl) is provided for the context. The specific source code is as follows:

@Configuration
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
class MailSenderPropertiesConfiguration {
    private final MailProperties properties;
    MailSenderPropertiesConfiguration(MailProperties properties) {
        this.properties = properties;
    }
    @Bean
    @ConditionalOnMissingBean
    public JavaMailSenderImpl mailSender() {
        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        applyProperties(sender);
        return sender;
    }
}

MailProperties is the configuration information about the mail server. The specific source code is as follows:

@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private String host;
    private Integer port;
    private String username;
    private String password;
    private String protocol = "smtp";
    private Charset defaultEncoding = DEFAULT_CHARSET;
    private Map<String, String> properties = new HashMap<>();
}

Open mail service

Log in to Netease mailbox 163, open and check POP3/SMTP/IMAP service in settings, and then you will get an authorization code, which will be used as login authentication.

Configure mail service

First, let's create the project springboot send mail through Spring Initializr, as shown in the figure:

Then we introduce web, thymeleaf and spring boot starter mail into pom.xml. For example:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>webjars-locator-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>3.3.7</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Fill in the relevant configuration information according to the previously mentioned configuration item (MailProperties), where spring.mail.username indicates the login account authenticated when connecting to the mail server, which can be an ordinary mobile phone number or login account, not necessarily an email. In order to solve this problem, it is recommended to fill in the email sender, that is, the real email, at spring.mail. properties.from.

Then add the following configuration in application.yml:

spring:
  mail:
    host: smtp.163.com #SMTP server address
    username:  socks #Login account
    password: 123456 #Login password (or authorization code)
    properties:
      from: socks@163.com #Mail sender (i.e. real mailbox)
  thymeleaf:
    cache: false
    prefix: classpath:/views/
  servlet:
    multipart:
      max-file-size: 10MB #Limit the size of a single file
      max-request-size: 50MB #Limit total requests

From the previous advanced knowledge, we know that before sending e-mail, we need to build SimpleMailMessage or MimeMessage e-mail information class to fill in e-mail title, e-mail content and other information, and finally submit it to JavaMailSenderImpl to send e-mail. This seems to be no problem and can achieve the set goal, but there will be a lot of scattered and repeated code in practical use, It is not easy to save mail to the database.

❞ ❝

So how to send email gracefully? The details of building information and sending e-mail should be shielded. Whether simple or complex e-mail, e-mail can be sent through a unified API. For example: mailService.send(mailVo)  .

For example, the mail subject, mail content and other information when sending mail are saved through the mail information class (MailVo):

package com.hehe.vo;

public class MailVo {
    private String id;    
    private String from;    
    private String to;    
    private String subject;    
    private String text;    
    private Date sentDate;    
    private String cc;    
    private String bcc;    
    private String status;    
    private String error;    
    @JsonIgnore    
    private MultipartFile[] multipartFiles;
}

Send mail and attachments

===========Next, let's formally introduce the core logic of sending e-mail=============

In addition to sending mail, it also includes operations such as detecting mail and saving mail, such as:

  • Check mail();   First, check the required items of mail recipient, mail subject and mail content. If it is empty, it will be rejected.

  • Send mail sendMimeMail();   Secondly, use MimeMessageHelper to parse MailVo and build MimeMessage to transmit mail.

  • Save the message sendMimeMail();   Finally, the mail is saved to the database to facilitate the statistics and tracking of mail problems.

The specific source code of mail service in this case is as follows:

package com.hehe.service;

@Service
public class MailService { 

    private Logger logger = LoggerFactory.getLogger(getClass());   
    @Autowired    
    private JavaMailSenderImpl mailSender;    
    public MailVo sendMail(MailVo mailVo) {        
        try {            
            checkMail(mailVo);             
            sendMimeMail(mailVo);             
            return saveMail(mailVo);         
        } catch (Exception e) {            
            logger.error("Failed to send mail:", e);            
            mailVo.setStatus("fail");            
            mailVo.setError(e.getMessage());            
            return mailVo;       
        }    
    }    

    private void checkMail(MailVo mailVo) {        
        if (StringUtils.isEmpty(mailVo.getTo())) {            
            throw new RuntimeException("Mail recipient cannot be empty");        
        }        

        if (StringUtils.isEmpty(mailVo.getSubject())) {            
            throw new RuntimeException("Message subject cannot be empty");        
        }        

        if (StringUtils.isEmpty(mailVo.getText())) {            
            throw new RuntimeException("Message content cannot be empty");        
        }    
    }    

    private void sendMimeMail(MailVo mailVo) {        
        try {            
            MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);            
            mailVo.setFrom(getMailSendFrom());            
            messageHelper.setFrom(mailVo.getFrom());            
            messageHelper.setTo(mailVo.getTo().split(","));            
            messageHelper.setSubject(mailVo.getSubject());            
            messageHelper.setText(mailVo.getText());            
            if (!StringUtils.isEmpty(mailVo.getCc())) {                
                messageHelper.setCc(mailVo.getCc().split(","));            
            }           

            if (!StringUtils.isEmpty(mailVo.getBcc())) {               
                messageHelper.setCc(mailVo.getBcc().split(","));            
            }            

            if (mailVo.getMultipartFiles() != null) {                
                for (MultipartFile multipartFile : mailVo.getMultipartFiles()) {                    
                    messageHelper.addAttachment(multipartFile.getOriginalFilename(), multipartFile);                
                }            
            }            

            if (StringUtils.isEmpty(mailVo.getSentDate())) {                
                mailVo.setSentDate(new Date());                
                messageHelper.setSentDate(mailVo.getSentDate());            
            }            

            mailSender.send(messageHelper.getMimeMessage());            
            mailVo.setStatus("ok");            
            logger.info("Mail sent successfully:{}->{}", mailVo.getFrom(), mailVo.getTo());        
        } catch (Exception e) {            
            throw new RuntimeException(e);        
        }    
    }    

    private MailVo saveMail(MailVo mailVo) {        
        return mailVo;   
    }    

    public String getMailSendFrom() {        
        return mailSender.getJavaMailProperties().getProperty("from");    
    }
}

After finishing the core business logic of sending e-mail, let's write a simple page to send e-mail.

First, write the MailController that interacts with the page. The specific source code is as follows:

@RestController
public class MailController {
    @Autowired
    private MailService mailService;

    @GetMapping("/")
    public ModelAndView index() {
        ModelAndView mv = new ModelAndView("mail/sendMail");
        mv.addObject("from", mailService.getMailSendFrom());
        return mv;
    }

    @PostMapping("/mail/send")
    public MailVo sendMail(MailVo mailVo, MultipartFile[] files) {
        mailVo.setMultipartFiles(files);
        return mailService.sendMail(mailVo);
    }
}

Then create sendMail.html in the / resources/views/mail directory. The specific source code is as follows:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8"/>
    <title>Send mail</title>
    <link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet" type="text/css"/>
    <script th:src="@{/webjars/jquery/jquery.min.js}"></script>
    <script th:href="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>

</head>

<body>
<div>
    <marquee behavior="alternate" onfinish="alert(12)"
             onMouseOut="this.start();$('#egg').text('hmm   How obedient! "); "
             onMouseOver="this.stop();$('#egg').text('Let me go!'); ">
        <h5>Happy New Year!</h5><img src="http://pics.sc.chinaz.com/Files/pic/faces/3709/7.gif" alt="">
    </marquee>

    <form>
        <div>
            <label>Mail sender:</label>
            <div>
                <input ${from}" readonly="readonly">
            </div>
        </div>
        <div>
            <label>Mail recipient:</label>
            <div>
                <input >
            </div>
        </div>
        <div>
            <label>Mail subject:</label>
            <div>
                <input >
            </div>
        </div>
        <div>
            <label>Mail content:</label>
            <div>
                <textarea ></textarea>
            </div>
        </div>
        <div>
            <label>Mail attachment:</label>
            <div>
                <input >
            </div>
        </div>
        <div>
            <label>Mail operation:</label>
            <div>
                <a onclick="sendMail()">Send mail</a>
            </div>
            <div>
                <a onclick="clearForm()">empty</a>
            </div>
        </div>
    </form>

    <script th:inline="javascript">
        var appCtx = [[${#request.getContextPath()}]];

        function sendMail() {

            var formData = new FormData($('#mailForm')[0]);
            $.ajax({
                url: appCtx + '/mail/send',
                type: "POST",
                data: formData,
                contentType: false,
                processData: false,
                success: function (result) {
                    alert(result.status === 'ok' ? "Sending succeeded!" : "You were Doge Ridiculed:" + result.error);
                },
                error: function () {
                    alert("Sending failed!");
                }
            });
        }

        function clearForm() {
            $('#mailForm')[0].reset();
        }

        setInterval(function () {
            var total = $('#mq').width();
            var width = $('#doge').width();
            var left = $('#doge').offset().left;
            if (left <= width / 2 + 20) {
                $('#doge').css('transform', 'rotateY(180deg)')
            }
            if (left >= total - width / 2 - 40) {
                $('#doge').css('transform', 'rotateY(-360deg)')
            }
        });
</script>
</div>
</body>
</html>

Test sending mail

If you are a beginner, it is recommended to modify the configuration and run the project according to my source code. After success, write the code again, which will help deepen your memory.

Start the project and visit: http://localhost:8080   Then you can see the main interface of sending mail as follows:

Then fill in your small email and click send email. If successful, you can log in to the small email to view the email and the attachments just uploaded.

So far, all the email codes have been completed. If you need the source code of this project, you can add my wechat: xttblog2 to obtain the source code for free.

Common failure codes

If the enterprise customizes the mail server, it will naturally record the mail log. Storing the log according to the error code is conducive to daily maintenance.

For example, these error code identifiers provided by Netease mailbox:

  • 421
    421 HL:REP the IP sending behavior is abnormal. There are a large number of non-existent recipients, and the connection is temporarily prohibited. Please check whether any users send viruses or spam, and check the validity of the sending list;
    421 HL:ICC the number of simultaneous concurrent connections of this IP is too large, exceeding the limit of Netease, and the connection is temporarily prohibited. Please check whether any users send viruses or spam, and reduce the number of IP concurrent connections;
    421 HL:IFC this IP has sent a large number of letters in a short time, which exceeds the limit of Netease and is temporarily prohibited from connecting. Please check whether any users send viruses or spam, and reduce the sending frequency;
    421 HL:MEP IP sending behavior is abnormal. There are a large number of behaviors of forging sending domain names, and the connection is temporarily prohibited. Please check whether any user sends virus or spam, and send it with a real and valid domain name;

  • 450
    450 MI:CEL sender has too many wrong instructions. Please check the sending procedure;
    450 Mi: the number of messages sent by DMC's current connection exceeds the limit. Please reduce the number of mails delivered in each connection;
    450 MI:CCL sender sends more instructions than normal. Please check the sending procedure;
    450 RP: the number of recipients sent by DRC over the current connection exceeds the limit. Please control the number of mail delivered per connection;
    450 RP:CCL sender sends more instructions than normal. Please check the sending procedure;
    450 DT:RBL sending IP is located in one or more rbls. Please refer to   http://www.rbls.org/   Information about RBL;
    450 WM:BLI this IP is not in the sending address list allowed by Netease;
    450 WM:BLU this user is not in the list of sending users allowed by Netease;

  • 451
    451 DT: SPM, please try again the email body is characterized by spam or the sending environment is not standardized, so it is temporarily rejected. Please keep the mail queue and re post the mail in two minutes. It is necessary to adjust the mail content or optimize the sending environment;
    451 Requested mail action not taken: too much fail authentication login failed too many times and was temporarily prohibited. Please check the password and account verification settings;
    451 RP:CEL sender has too many wrong instructions. Please check the sending procedure;
    451 Mi: the number of messages sent by DMC over the current connection exceeds the limit. Please control the number of mails delivered in each connection;
    451 Mi: the number of SFQ senders exceeds the limit within 15 minutes, please control the sending frequency;
    451 RP: the cumulative number of recipients of QRC sender in a short period exceeds the limit, and the sender is temporarily prohibited from sending letters. Please reduce the sending frequency of the user;
    • 451 Requested action aborted: local error in processing system temporarily fails, please try sending again later;

  • 500
    500 error: syntax error of smtp command sent by bad syntax u;
    550 MI:NHD HELO command cannot be null;
    550 Mi: the email address of the IMF sender is not standard. Please refer to   http://www.rfc-editor.org/   Definition of e-mail specification;
    550 Mi: the SPF sending IP is not licensed by the SPF of the sending domain. Please refer to   http://www.openspf.org/   Definition of SPF specification;
    550 MI:DMA this message is not licensed by the DMARC of the sending domain. Please refer to   http://dmarc.org/   Definition of DMARC specification;
    550 Mi: the number of connections of STC sender on the same day exceeds the limit, and the sender's mail will not be accepted on the same day. Please control the number of connections;
    550 RP:FRL Netease mailbox does not open anonymous forwarding (Open relay);
    550 RP: the number of recipients of RCL group email exceeds the limit, please reduce the number of recipients of each email;
    550 RP: the cumulative number of recipients of TRC sender in the same day exceeds the limit, and the sender's mail will not be accepted on the same day. Please reduce the sending frequency of the user;
    550 DT:SPM email body has many spam features or the sending environment is lack of standardization. It is necessary to adjust the mail content or optimize the sending environment;
    550 Invalid User the requested user does not exist;
    550 User in blacklist: the user is not allowed to send letters to Netease users;
    550 User suspended the user requested is disabled or frozen;
    550 requested mail action not taken: the number of too much recipients exceeds the limit;

  • 552
    552 legal attachment is not allowed to send attachments of this type, including attachments ending with. UU. PIF. SCR. MIM. HQX. BHX. CMD. VBS. Bat. Com. VBE. VB. JS. WSH, etc;
    552 Requested mail action aborted: exceeded mailsize limit;

  • 553
    553 Requested action not taken: NULL sender is not allowed. The sender is not allowed to be empty. Please use the real sender to send;
    553 Requested action not taken: Local user only SMTP machines only allow the sender to be the user of this site;
    553 Requested action not taken: no smtp MX only MX type machines do not allow the sender to be a user of this site;
    553 authentication is required SMTP requires authentication, please check the client settings;

  • 554
    554 DT: the message sent by SPM contains unlicensed information, or is recognized as spam by the system. Please check whether any users send viruses or spam;
    554 DT: the sender and sender of sum envelope do not match;
    554 IP is rejected, SMTP auth error limit exceeded this IP has failed authentication too many times and is temporarily prohibited from connecting. Please check the authentication information settings;
    554 HL:IHU sending IP is temporarily suspended due to sending spam or abnormal connection behavior. Please check the sending status of the sending IP in history and whether the sending program is abnormal;
    554 HL:IPB. This IP is not in the sending address list allowed by Netease;
    554 Mi: the cumulative number of messages sent by STC sender in the same day exceeds the limit, and the sender's messages will not be accepted on the same day. Please reduce the sending frequency;
    554 MI:SPB this user is not in the list of sending users allowed by Netease;
    554 IP in blacklist this IP is not in the sending address list allowed by Netease.

Tags: Java

Posted on Sun, 31 Oct 2021 20:55:17 -0400 by frabble