栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

ASP.NET WebApi E-Mail服务(.NET 6.0+EntityFrameworkCore 6)

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ASP.NET WebApi E-Mail服务(.NET 6.0+EntityFrameworkCore 6)

文章目录
  • 目标
  • 创建WebApi
    • 一.创建数据库
      • 1.数据表构成
      • 2.SQL脚本
    • 二.链接EFCore并进行依赖注入
    • 三.代码解释
      • 1.重写控制器基类
      • 2.创建控制器
      • 3.返回值Models类
      • 4.Config
        • (1).Config.cs
        • (2).Config的模型类
        • (3).platform.json
      • 5.外部WebApi的调用
        • (1).ExternWebApiBase.cs
        • (2).Address类
      • 6.MailPlatform
        • (1).MailBuilder
        • (2).MailPlatform
      • 7.全局引用(NET6.0)
    • 三.需要导入的包
  • 项目文件下载
  • EMail-API服务

目标

通过SMTP向请求邮箱发信,发信邮箱由API建立者创建,并设置权限

创建WebApi 一.创建数据库 1.数据表构成

TB_RequestLog

列名类型特征备注
Indexint主键 自动增长 非空编号
Regionnchar(32)非空控制器名称
Actionnchar(32)非空函数名称
Argumentsvarchar(max)非空参数集
IPnchar(16)请求者IP
Addressnchar(32)请求者地理位置
Identitynchar(11)请求者身份验证
LogTimedatetime非空记录时间

TB_Mail

列名类型特征备注
Guidnchar(16)非空 主键唯一UID
Addressnchar(32)非空目前邮箱
Titlenvarchar(max)非空标题
Bodynvarchar(max)非空主体
Allocatednchar(32)非空分配的主体号码
SendTimedatetimt非空发生时间
2.SQL脚本
USE [db_spaceserver]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TB_Mail](
	[Guid] [nchar](16) NOT NULL,
	[Address] [nchar](32) NOT NULL,
	[Title] [nvarchar](max) NOT NULL,
	[Body] [nvarchar](max) NOT NULL,
	[Allocated] [nchar](32) NOT NULL,
	[SendTime] [datetime] NOT NULL,
 CONSTRAINT [PK_TB_Mail] PRIMARY KEY CLUSTERED 
(
	[Guid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TB_RequestLog](
	[Index] [int] IDENTITY(1,1) NOT NULL,
	[Region] [nchar](32) NOT NULL,
	[Action] [nchar](32) NOT NULL,
	[Arguments] [varchar](max) NULL,
	[IP] [nchar](16) NULL,
	[Address] [nchar](32) NULL,
	[Identity] [nchar](11) NULL,
	[LogTime] [datetime] NOT NULL,
 CONSTRAINT [PK_TB_RequestLog] PRIMARY KEY CLUSTERED 
(
	[Index] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
二.链接EFCore并进行依赖注入

过程参照之前连接EFCore的步骤
EFCore的链接生成上下文和模型
完成后进行依赖注入
(笔者使用的是.NET6.0的环境,配置项在Program.cs里面,如果使用之前的版本,配置项在Startup.cs里面)

builder.Services.AddSqlServer(builder.Configuration.GetConnectionString("DB_SpaceServer"));

记着在appsettings.json里面提前配置好链接字符串
然后进行以下配置

builder.Services.AddControllersWithViews().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All);
});
builder.Services.AddRouting(options => options.LowercaseUrls = true); 

上文第一个配置可以使得Json的序列化不会出现编码错误,第二个可以让URL全小写

三.代码解释 1.重写控制器基类

首先我们继承一个控制器基类来代替ControllerBase,因为我们要使用到很多特定的方法,基类化了过后可以为编程提供很多便利

public class ControllerBaseEx : ControllerBase,IDisposable
    {
        protected Db_SpaceserverContext DBContext { set; get; } 
        protected string RequestIP
        {
            get
            {
                var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
                if (string.IsNullOrEmpty(ip))
                {
                    ip = HttpContext.Connection.RemoteIpAddress.ToString();
                }
                return ip;
            }
            
        }
        protected Config Config { get; } = new Config();
        protected List WaitTasks = new List();
        protected ControllerBaseEx(Db_SpaceserverContext context)
        {
            DBContext = context;
        }
        void IDisposable.Dispose()
        {
            //等待所有Task执行完毕
            foreach (var c in WaitTasks) c.Wait();
        }
        protected void Log(object args,string identity = "")
        {
            Address address = new();
            string region = (ControllerContext.RouteData.Values["controller"] ?? "NULL").ToString();
            string action = (ControllerContext.RouteData.Values["action"] ?? "NULL").ToString();
            var log = new TbRequestLog()
            {
                Action = action,
                Region = region,
                Ip = RequestIP.Length < 5 ? "" : RequestIP,
                Address = RequestIP.Length < 5 ? "" : address.GetAddress(RequestIP),
                LogTime = DateTime.Now,
                Identity = identity,
                Arguments = JsonConvert.SerializeObject(args)
            };
            DBContext.TbRequestLog.Add(log);
            DBContext.SaveChanges();
            
        }
        protected void Log(string args, string identity = "")
        {
            Address address = new();
            string region = (ControllerContext.RouteData.Values["controller"] ?? "NULL").ToString();
            string action = (ControllerContext.RouteData.Values["action"] ?? "NULL").ToString();
            var log = new TbRequestLog()
            {
                Action = action,
                Region = region,
                Ip = RequestIP.Length < 5 ? "" : RequestIP,
                Address = RequestIP.Length < 5 ? "" : address.GetAddress(RequestIP),
                LogTime = DateTime.Now,
                Identity = identity,
                Arguments = args
            };
            DBContext.TbRequestLog.Add(log);
            DBContext.SaveChanges();
        }
        protected string JsonString(object args) => JsonConvert.SerializeObject(args);
        protected ActionResult Json(object args) => new JsonResult(args);
        protected BadRequestObjectResult Error(string result) => BadRequest(new { code = 400, result = result });
        protected string Random(int length,string dic = "")
        {
            dic = dic == "" ? "qwertyuiopasdfghjklzxcvbnm0123456789QWERTYUIOPASDFGHJKLZXCVBNM" : dic == "-1" ? "0123456789" : dic;
            string data = "";Random rand = new Random();
            for(int i= 0; i < length; i++)
            {
                data += dic[rand.Next(dic.Length)];
            }
            return data;
        }

      
    }

字段值


DBContext EFCore上下文对象,通过继承后使用注入对象初始化
RequestIP 请求IP,实际上是获取表头中的X-Forwarded-For字段,如果本地请求,这个值为’:::’
Config 用于获取其他配置文件
WaitTasks 用于保存所有异步task


函数


protected ControllerBaseEx(Db_SpaceserverContext context)

参数名类型说明
contextDb_SpaceserverContext用于初始化DBContext属性

初始化函数


void IDisposable.Dispose()

释放对象时用于等待所有的task对象


protected void Log(object args,string identity = “”)

参数名类型说明
argsobject参数object对象
identitystring请求者身份验证

protected void Log(string args,string identity = “”)

参数名类型说明
argsstring序列化后的参数集
identitystring请求者身份验证

用于记录LOG信息


protected string JsonString(object args)

参数名类型说明
argsobject需要转换的数据

用于序列化对象


protected ActionResult Json(object args)

参数名类型说明
argsobject返回的json object

用于返回JsonResult


protected BadRequestObjectResult Error(string result)

参数名类型说明
resultstring错误原因

用于返回BadRequest


protected string Random(int length,string dic = “”)

参数名类型说明
lengthint生成长度
dicstring生成字典

用于生成指定长度的随机序列,如果dic为空,就从全字典选择,为-1,就生成数字集,其他为指定集


2.创建控制器

控制器名称MailCodeController.cs

	[Route("api/[controller]/[action]")]
    [ApiController]
    public class MailCodeController : ControllerBaseEx
    {
        private ILogger _logger;
        private MailPlatformSetting _setting;
        public MailCodeController(Db_SpaceserverContext context,ILogger logger) : base(context) 
        {
            _logger = logger;
            _setting = Config.GetSetting("platform.json");
        }

        [HttpGet]
        public async Task SendCode(string code,string office,string address)
        {
            try
            {
                //写入log
                object log = new
                {
                    code,
                    office,
                    address
                };
                Log(JsonString(log), "SYSTEM");

                //判断信息
                if (code.Length > 10) throw new Exception("超过最大内容长度,10位");
                if (office.Length > 16) throw new Exception("超过最大的签名长度,16位");

                //判断邮箱是否符合
                Regex regex = new(@"^[A-Za-z0-9u4e00-u9fa5]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$");
                if (!regex.IsMatch(address)) throw new Exception("邮箱地址不符合");

                //生成GUID与时间
                string guid = Random(16);
                DateTime time = DateTime.Now;

                //创建平台
                MailPlatform platform = new(_setting.Platform.ToArray());

                //创建信息
                MailBuilder builder = new MailBuilder();
                builder.Address.Add(address);
                builder.Subject = $"来自{office}的验证码";
                builder.Body = $"您的验证码为:{code},发送方为:{office},发送时间为:{time}";

                //发送邮件
                var task = platform.SendMailAsync(builder.Build());
                WaitTasks.Add(task);

                //写入记录
                TbMail mail = new TbMail()
                {
                    Address = address,
                    Allocated = platform.LastAllocatedMail,
                    Body = builder.Body,
                    Title = builder.Subject,
                    Guid = guid,
                    SendTime = time,
                };

                //保存到数据库
                DBContext.TbMail.Add(mail);
                DBContext.SaveChanges();
                

                //生成返回集
                MailCodeModel model = new()
                {
                    Address = address,
                    Content = $"-H {builder.Subject} -B {builder.Body}",
                    Guid = guid,
                    DateTime = time,
                };
                return Json(model);
            }
            catch(Exception ex)
            {
                return Error(ex.Message);
            }
        }
    }

字段值


_logger WebApi自带log对象
_setting MailPlatform的setting对象


函数


public MailCodeController(Db_SpaceserverContext context,ILogger logger)

参数名类型说明
contextDb_SpaceserverContext注入上下文对象
loggerILogger注入log对象

初始化函数


[HttpGet]
public async Task SendCode(string code,string office,string address)

参数名类型说明
codestring验证码
officestring公司名称
addressstring目标邮箱

验证码发信函数


3.返回值Models类
public class MailCodeModel
    {
        public string Guid { set; get; }
        public string Address { set; get; }
        public string Content { set; get; }
        public DateTime DateTime { set; get; }

    }

字段值


Guid 发信标记唯一Uid
Address 目标邮箱
Content 内容
DateTime 发信时间


4.Config (1).Config.cs
public class Config
    {
        public T GetSetting(string path)where T:class
        {
            StreamReader reader = new(path);
            var data = reader.ReadToEnd();
            reader.Close();
            return JsonConvert.DeserializeObject(data);
        }
       
    }

函数


public T GetSetting(string path)where T:class

参数名类型说明
pathstringjson文件地址
TT配置文件模型

获取配置文件并转为.NET对象


(2).Config的模型类
public class MailPlatformSetting
    {
        public List Platform { get; set; }
        public int Count => Platform.Count;
    }
    public class EMailSetting
    {
        public string EMail { get; set; }
        public string Key { get; set; }
    }
(3).platform.json
{
  "Platform": [
    {
      "EMail": "邮箱",
      "Key": "授权码"
    },
    {
      "EMail": "邮箱",
      "Key": "授权码"
    },
    {
      "EMail": "邮箱",
      "Key": "授权码"
    }
  ]
}
5.外部WebApi的调用 (1).ExternWebApiBase.cs
public class ExternWebApiBase
    {
        protected string RequestByGet(string baseurl,params string[] args)
        {
            string url = baseurl + "?";
            url += string.Join('&', args);            
            HttpClient client = new HttpClient();           
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
            HttpResponseMessage response = client.Send(request);
            return response.Content.ReadAsStringAsync().Result;            
        }
        protected T RequestByGet(string baseurl, params string[] args)
        {
            string url = baseurl + "?";
            url += string.Join('&', args);
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
            HttpResponseMessage response = client.Send(request);
            return JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
        }
        protected string RequestByPost(string baseurl, object args)
        {
            string url = baseurl;
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
            //拼凑json
            JsonContent content = JsonContent.Create(args);
            request.Content = content;
            HttpResponseMessage response = client.Send(request);
            return response.Content.ReadAsStringAsync().Result;
        }
        protected T RequestByPost(string baseurl, object args)
        {
            string url = baseurl;
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
            //拼凑json
            JsonContent content = JsonContent.Create(args);
            request.Content = content;
            HttpResponseMessage response = client.Send(request);
            return JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
        }
    }
(2).Address类
 public class Address: ExternWebApiBase
    {
        public string GetAddress(string ip)
        {
            string url = "http://whois.pconline.com.cn/ip.jsp";
            string data = RequestByGet(url,
                $"ip={ip}",
                $"level=3"
                );
            return data;
        }
    }
6.MailPlatform

MailPlatform的实现
但是又不同于上方博客,我们要稍微改一下

(1).MailBuilder
public class MailBuilder
    {
        public MailBuilder() { }
        public List? Address { set; get; } = new List();
        public string? Body { set; get; } = string.Empty;
        public Encoding? Encoding { set; get; } = Encoding.UTF8;
        public bool? IsHtml { set; get; } = true;
        public string? Subject { set; get; } = string.Empty;
        public List? Attachments { set; get; } = new List();
        public MailMessage Build()
        {
            var message = new MailMessage();
            if (Address?.Count == 0) throw new Exception("发送人不能为空");
            foreach (var c in Address)
            {
                message.To.Add(c);
            }
            message.Subject = Subject;
            message.Body = Body;
            message.IsBodyHtml = IsHtml ?? true;
            message.BodyEncoding = Encoding;
            message.Priority = MailPriority.Normal;
            message.SubjectEncoding = Encoding;
            foreach(var c in Attachments)
            {
                if (!File.Exists(c)) throw new Exception("附件文件不存在");
                var data = new Attachment(c, MediaTypeNames.Application.Octet);//实例化附件 
                data.ContentDisposition.CreationDate = File.GetCreationTime(c);
                data.ContentDisposition.ModificationDate = File.GetLastAccessTime(c);
                data.ContentDisposition.ReadDate = DateTime.Now;
                message.Attachments.Add(data);//添加到附件中 
            }
            return message;
        }
        public Task BuildAsync()
        {
            Task message = new Task(() =>
            {
                var message = new MailMessage();
                if (Address?.Count == 0) throw new Exception("发送人不能为空");
                foreach (var c in Address)
                {
                    message.To.Add(c);
                }
                message.Subject = Subject;
                message.Body = Body;
                message.IsBodyHtml = IsHtml ?? true;
                message.BodyEncoding = Encoding;
                message.Priority = MailPriority.Normal;
                message.SubjectEncoding = Encoding;
                foreach (var c in Attachments)
                {
                    if (!File.Exists(c)) throw new Exception("附件文件不存在");
                    var data = new Attachment(c, MediaTypeNames.Application.Octet);//实例化附件 
                    data.ContentDisposition.CreationDate = File.GetCreationTime(c);
                    data.ContentDisposition.ModificationDate = File.GetLastAccessTime(c);
                    data.ContentDisposition.ReadDate = DateTime.Now;
                    message.Attachments.Add(data);//添加到附件中 
                }
                return message;
            });
            message.Start();
            return message;
        }
        
    }
(2).MailPlatform
public class MailPlatform
    {
        private string _last;
        public string LastAllocatedMail { get => _last; }
        private List<(MailAddress,string)>? _mailinformation;
        public MailPlatform(params (MailAddress,string)[]? pairs)
        {
            _mailinformation = pairs.ToList() ?? throw new Exception("错误的初始化");
        }
        public MailPlatform(params EMailSetting[]? pairs)
        {
            _mailinformation = new List<(MailAddress, string)>();
            foreach(var c in pairs)
            {
                (MailAddress, string) data = (new MailAddress(c.EMail), c.Key);
                _mailinformation.Add(data);
            }
            if (_mailinformation.Count == 0) throw new Exception("错误的初始化");

        }
        public void SendMail(MailMessage message)
        {
            var random = new Random();
            var info = _mailinformation?.OrderBy(s => Guid.NewGuid()).FirstOrDefault() ?? throw new Exception("未初始化任何设置");
            _last = info.Item1.Address;
            message.From = info.Item1;
            var client = new SmtpClient()
            {
                EnableSsl = true,
                UseDefaultCredentials = false,
                Credentials = new System.Net.NetworkCredential(info.Item1.Address, info.Item2),
                DeliveryMethod = SmtpDeliveryMethod.Network,
                Host = "smtp." + info.Item1.Host
            };
            client.Send(message);
        }
        public void SendMailAsync(MailMessage message, SendCompletedEventHandler CompletedMethod, object args)
        {
            var random = new Random();
            var info = _mailinformation?.OrderBy(s => Guid.NewGuid()).FirstOrDefault() ?? throw new Exception("未初始化任何设置");
            _last = info.Item1.Address;
            message.From = info.Item1;
            var client = new SmtpClient()
            {
                EnableSsl = true,
                UseDefaultCredentials = false,
                Credentials = new System.Net.NetworkCredential(info.Item1.Address, info.Item2),
                DeliveryMethod = SmtpDeliveryMethod.Network,
                Host = "smtp." + info.Item1.Host
            };
            client.SendCompleted += new SendCompletedEventHandler(CompletedMethod);
            client.SendAsync(message, args);
        }
        public Task SendMailAsync(MailMessage message)
        {
            var random = new Random();
            var info = _mailinformation?.OrderBy(s => Guid.NewGuid()).FirstOrDefault() ?? throw new Exception("未初始化任何设置");
            _last = info.Item1.Address;
            Task task = new Task(() =>
              {               
                  message.From = info.Item1;
                  var client = new SmtpClient()
                  {
                      EnableSsl = true,
                      UseDefaultCredentials = false,
                      Credentials = new System.Net.NetworkCredential(info.Item1.Address, info.Item2),
                      DeliveryMethod = SmtpDeliveryMethod.Network,
                      Host = "smtp." + info.Item1.Host
                  };
                  client.Send(message);
              });
            task.Start();
            return task;        
        }
    }

因为要获取Allocate信息,需要调用LastAllocatedMail属性

7.全局引用(NET6.0)

GlobalUsing.cs

//SpaceServer函数
global using SpaceServer.EFCore;
global using SpaceServer.EFCore.Models;
global using SpaceServer.Programs.Mail;
global using SpaceServer.Programs.Address;
global using SpaceServer.Programs.Config;
global using SpaceServer.Programs.Config.Models;
global using SpaceServer.Models;

//Mincrosoft函数
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.Http;

//System函数
global using System.Net;
global using System.IO;
global using System.Text;
global using System.Text.RegularExpressions;

//外部函数
global using Newtonsoft.Json;
global using Newtonsoft.Json.Linq;
三.需要导入的包

项目文件下载

项目文件下载


EMail-API服务

本API只能用于发验证码测试,不用于其他方面,一切问题与作者无关
请求地址: api.spaceserver.cloud/api/mailcode/sendcode?
请求方法:GET
参数集

参数名类型说明
codestring发送的验证码
officestring公司名称
addressstring目标邮箱地址

返回集

参数名类型说明
Guidstring唯一标识符
Addressstring目标邮箱地址
Contentstring内容
DateTimeDateTime发送时间

错误集

参数名类型说明
codeint错误代码
resultstring错误原因

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/906576.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号