8.邮件发送功能

打印 上一主题 下一主题

主题 815|帖子 815|积分 2445

这里想补充下上个文章,感觉有点不太行。因为每次设计新的表结构就要去更新一下,所以,干脆随着我要做的功能去展示我的表结构设计,最终再把所有的表结构包括sql语句统计出来,感觉这样更新会方便很多~
这个文章主要是发送邮件的功能。之前提过,我不是一下子把后端全部完成,然后再一下子搞定后端。所以我前后端是要一起做,然后去完善接口功能。
登录注册这个功能,90%网站都需要。可以不用,但是不能没有。本篇文章主要是实现注册的验证码发放功能(邮件or手机号)。
因为发短信得收费,咳咳,所以这里先做的邮件发送验证码。这个在网络冲浪的时候要注册某些网站账号的时候一般都会遇上把?之前看有的朋友是后台返回一张验证码的图片,这个我后面应该也会做一下,但我觉得意义不是特别大。就跟死验证一样,这样我怎么判定你输入的邮箱是否是正确真实可用的呢?我不想给数据库搞一堆脏数据,麻烦
之前微软是自带一个邮箱发送的库的,现在好像弃用了,虽然还可以用。但是微软推荐了一个新的开源的第三方库MailKit。使用起来相对来说,代码减少了不少,感觉挺好用的,官网也有案例,所以不会细说,官网的很详细,我也不想写博客都是搬运官网的。我的代码会加上自己的注释。哪怕你不看官网,跟着我这个流程走就100%没问题。
正文开始~ 
先展示下,验证码表的数据库结构:注释说明都有应该无需细说了

 建表SQL
  1. create table  dbo.AutoCode
  2. (
  3.        Id                INT identity(1, 1) not null  /*编号*/,
  4.        Code              VARCHAR(20) not null  /*验证码*/,
  5.        Phone             VARCHAR(20) null  /*手机号*/,
  6.        Email             VARCHAR(100) null  /*邮箱*/,
  7.        ExpDate           FLOAT not null  /*过期时间*/,
  8.        CreateDate        DATETIME default getdate() null  /*创建日期*/,
  9.        UpdateDate        DATETIME default getdate() null  /*修改日期*/
  10. );
  11. alter  table dbo.AutoCode
  12.        add constraint PK_AutoCode_Id primary key (Id);
  13. EXEC sp_addextendedproperty 'MS_Description', '验证码表', 'user', dbo, 'table', AutoCode, NULL, NULL;
  14. EXEC sp_addextendedproperty 'MS_Description', '编号', 'user', dbo, 'table', AutoCode, 'column', Id;
  15. EXEC sp_addextendedproperty 'MS_Description', '验证码', 'user', dbo, 'table', AutoCode, 'column', Code;
  16. EXEC sp_addextendedproperty 'MS_Description', '手机号', 'user', dbo, 'table', AutoCode, 'column', Phone;
  17. EXEC sp_addextendedproperty 'MS_Description', '邮箱', 'user', dbo, 'table', AutoCode, 'column', Email;
  18. EXEC sp_addextendedproperty 'MS_Description', '过期时间 (当前时间+过期时间)的时间戳', 'user', dbo, 'table', AutoCode, 'column', ExpDate;
  19. EXEC sp_addextendedproperty 'MS_Description', '创建日期 默认为当前时间', 'user', dbo, 'table', AutoCode, 'column', CreateDate;
  20. EXEC sp_addextendedproperty 'MS_Description', '修改日期 默认为当前时间', 'user', dbo, 'table', AutoCode, 'column', UpdateDate;
复制代码
View Code 使用SQLsugar生成实体到FastEasy.Model类库中。
这里插一句吧,前面不是用T4模板搞了个生成仓储模式4个类的代码生成吗,我可能目前对于T4还是不是很会,在T4模板使用第三方库的时候会报一个找不到netstand程序集的问题,我目前很疑惑。然后看了下别人的代码生成器,好像通过后台管理系统就可以,那目前可能我弄的这个还不是很好,但是也极大的减少了一些工作量,以后我会再学习下这块儿,也搞个更方便的代码生成器。T4模板确实有一些局限性……
那我是又在base基类添加了一个方法,CreateModels。我这个数据库空空如也,所以选择了全部生成,当然也可以根据条件去筛选生成,甚至重命名,修改字段名,诸如此类的。这是sqlsugar提供的,真的很强大,可以去学习一下。中秋的时候阿妮亚又更新了一个功能,可以把增删改写成一个方法,但我目前还是没看,只是看到了介绍,这个以后再去看一下,nb~

代码只有一句:第一个参数是生成的目录路径,第二个是命名空间
  1.     public void CreateModels()
  2.     {
  3.         db.DbFirst.IsCreateAttribute().CreateClassFile(@"D:\FastEasy\API\FastEasy.Model\Models", "FastEasy.Model.Models");
  4.     }
复制代码
 如何使用呢。因为是要继承base基类,所以得有个实体,所以我就手动创建了一个空的base类。然后用T4代码生成器分别生成了仓储层和服务层的接口和实现类。主要是避免没有泛型传入,注入的时候报错。然后直接调用这个方法就好了,base类在这里没有作用!只是避免注入解析的时候不报错!
其实应该稍微感觉的出来这样好像有点麻烦……无奈现在手里活不够多,经验欠缺,暂时没有想到别的思路,但我们有代码生成器,终归也麻烦不到哪去,一分钟就搞定了

 到这里,我们生成了实体,然后通过T4代码生成器,同样根据数据库生成了各自的仓储层和服务层。这里就不得不提一个弊端了。但是这只能说目前的,就是要先生成实体,因为T4模板不需要运行就可以生成,所以没实体的话,依赖注入会找不到实体而报错,这样就没法运行程序生成实体了。除非就是说,咱们搞个控制台程序单独运行下?反正上边也提到了,这个T4我弄的确实不到位,只是最基础的一些功能,还需继续完善呀~~~
 
言归正传,这篇文章主要说的是发送邮件!
上面简单提到了,我们使用的是微软推荐的一个第三方库:MailKit+MimeKit。NuGet管理器自行下载。
MailKit是用于向邮箱服务端发送消息的。
MimeKit则是创建邮件实例的(包括并不局限:发送人接收人邮件内容等……)。
再简单点理解就是,通过MailKIt把MimeKit创建的邮件实例发送给邮箱服务端,然后根据你的邮件实例解析发送相应的邮件。似乎我这么说有点绕嘴,但我是这么理解的。甚至一开始我都很好奇,为什么要分成两个包安装呀,或者你内置进去呗?不过他这些个库不仅仅支持你发送邮件,好像还能下载邮件,删除邮件,挺多功能的,确实np,但这些我好像也用不上,有兴趣的同学就自行学习吧~
实现注册发送邮件验证码的功能。其实很简单。首先搞清楚,我们需要的情景是如何的:

  • 用户填写邮箱
  • 发送验证码到邮箱
  • 正则匹配输入的是否正确格式
  • 生成随机验证码
  • 新增到数据库
  • 返回结果给前端成功还是失败
正则也没啥可说的,因为你百度邮箱正则就找到了匹配规则拿过来用就好了。随机验证码这个更没什么可说的Random……当然你可以搞更复杂的,但我觉得我这样应该足够了。
我把这两个方法写在了Common类库中。因为他们不涉及业务逻辑,是可公用的。因此做成静态类方法直接调用就好。
  1.         /// <summary>
  2.         /// 检验是否手机号
  3.         /// </summary>
  4.         /// <param name="number">号码</param>
  5.         /// <returns></returns>
  6.         public static bool IsPhone(string number)
  7.         {
  8.             string phoneRegex = @"^1\d{10}$";
  9.             return Regex.IsMatch(number, phoneRegex);
  10.         }
  11.         /// <summary>
  12.         /// 检验是否邮箱
  13.         /// </summary>
  14.         /// <param name="number">号码</param>
  15.         /// <returns></returns>
  16.         public static bool IsEmail(string number)
  17.         {
  18.             string emailRegex = @"^\S+@\S+\.\S+$";
  19.             return Regex.IsMatch(number, emailRegex);
  20.         }
复制代码
  1.         /// <summary>
  2.         /// 创建一个4位随机验证码
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         public static string CreateRandomCode()
  6.         {
  7.             int num = 4;
  8.             const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 根据需要修改字符集合
  9.             Random random = new();
  10.             char[] code = new char[num];
  11.             for (int i = 0; i < num; i++)
  12.             {
  13.                 code[i] = chars[random.Next(chars.Length)];
  14.             }
  15.             return new string(code);
  16.         }
复制代码
 
包括发送邮件的功能,也理所应当的放在Common类库中。目前因为针对发送邮件的功能,可能封装的方法自由度不高,我贴的代码只做参考,可以根据自己需求完善邮件实例是用MimeKit中的MimeMessage创建的。里面包含了发送人,接收人,邮件内容等。原本我想传递这个类型的参数,但是官方的这个实例中,发送人和接收人是不能直接赋值的,要Add添加进去。这对我们来说还是有点麻烦的 ,主要是传参太麻烦了。所以自己创建了一个新的实体类型Mime,主要用来传递参数。
  1. using MimeKit;
  2. namespace FastEasy.Common.Extension.Mail
  3. {
  4.     public class Mime
  5.     {
  6.         /// <summary>
  7.         /// 主题
  8.         /// </summary>
  9.         public string Subject { get; set; }
  10.         /// <summary>
  11.         /// 消息内容
  12.         /// </summary>
  13.         public TextPart Body { get; set; }
  14.         /// <summary>
  15.         /// 发送人,默认为“官方”
  16.         /// </summary>
  17.         public MailboxAddress From { get; set; } = new MailboxAddress("神里凌华", "XXXXXX@163.com");
  18.         /// <summary>
  19.         /// 邮箱SMTP授权码
  20.         /// </summary>
  21.         public string Code { get; set; } = "XXXXX";
  22.         /// <summary>
  23.         /// 接收人
  24.         /// </summary>
  25.         public MailboxAddress To { get; set; }
  26.         /// <summary>
  27.         /// 465加密25不加密
  28.         /// </summary>
  29.         public  int Port { get; set; } = 465;
  30.     }
  31. }
复制代码
 发送邮件方法:通过上面的注释应该都可以看懂,所以不过多解释废话了
  1.         /// <summary>
  2.         /// 发送邮件
  3.         /// </summary>
  4.         /// <param name="mime">邮件消息体</param>
  5.         /// <returns></returns>
  6.         public static async Task<bool> SendMail(Mime mime)
  7.         {
  8.             //邮件实例
  9.             MimeMessage message = new()
  10.             {               
  11.                 Subject = mime.Subject,
  12.                 Body = mime.Body,
  13.             };
  14.             message.To.Add(mime.To);
  15.             message.From.Add(mime.From);
  16.             try
  17.             {
  18.                 using var client = new SmtpClient();
  19.                 client.Connect("smtp.163.com", mime.Port, mime.Port == 465);
  20.                 client.Authenticate(mime.From.Address, mime.Code);
  21.                 await client.SendAsync(message);
  22.                 client.Disconnect(true);
  23.             }
  24.             catch
  25.             {
  26.                 return false;
  27.             }
  28.             return true;
  29.         }
复制代码
 
由此看来,该功能并未涉及程序本身的业务逻辑,因此只要在API程序层调用相应的公共方法,然后调用基类的新增方法即可完成。API程序层代码: 
  1.         /// <summary>
  2.         /// 发送验证码
  3.         /// </summary>
  4.         /// <param name="user">邮箱或者手机号</param>
  5.         /// <returns></returns>
  6.         [HttpPost]
  7.         [SwaagerRoute(SwaggerVersion.FastEasy, "CreateCode")]
  8.         public async Task<ResultDto> CreateCode(string user)
  9.         {
  10.             var result = new ResultDto();
  11.             var code = RandomCode.CreateRandomCode();
  12.             if (RegexMatch.IsPhone(user))
  13.             {
  14.                 result.State = false;
  15.                 result.Message = "暂不支持手机号注册";
  16.             }
  17.             else if (RegexMatch.IsEmail(user))
  18.             {
  19.                 var mime = new Mime
  20.                 {
  21.                     Body = new MimeKit.TextPart()
  22.                     {
  23.                         Text = code
  24.                     },
  25.                     Subject = "验证码",
  26.                     To = new MimeKit.MailboxAddress(user, user),
  27.                 };
  28.                 var autocode = new AutoCode
  29.                 {
  30.                     Code = code,
  31.                     CreateDate = DateTime.Now,
  32.                     Email = user,
  33.                     ExpDate = DateTimeOffset.Now.AddMinutes(10).ToUnixTimeSeconds(),
  34.                     UpdateDate = DateTime.Now
  35.                 };
  36.                 if (await EmailKit.SendMail(mime) && await service.CreateEntityAsync(autocode))
  37.                 {
  38.                     result.State = true;
  39.                     result.Message = "发送成功请查收";
  40.                 }
  41.                 else
  42.                 {
  43.                     result.State = false;
  44.                     result.Message = "发送失败";
  45.                 }
  46.             }
  47.             else
  48.             {
  49.                 result.State = false;
  50.                 result.Message = "请输入正确的注册号码";
  51.             };
  52.             return result;
  53.         }
复制代码
我承认我废话太多了,我本意想讲的详细点……看效果:

 掰掰,中秋国庆快乐……我还没买到票……
    

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表