前言

之前介绍的友情链接功能,只实现了友情链接的展示和管理接口。

还缺失友情链接申请、审核管理、通知,现在把这块功能补全。

Model 什么的之前那篇文章都有,本文直接补全逻辑代码~

详见: 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能

先看效果

友情链接申请页面

邮件通知

实现一个简单的通知功能,申请通过之后,给申请友链的邮箱发通知。

使用 MimeKit 这个库可以很方便的实现发邮件功能

为了更方便使用,我封装了一个 EmailUtils 放在 StarBlog.Share.Utils 里面

public class EmailAccountConfig {
public string Host { get; set; }
public int Port { get; set; }
public string FromUsername { get; set; }
public string FromPassword { get; set; }
public string FromAddress { get; set; }
} public static class EmailUtils {
public static async Task<MessageSentEventArgs> SendEmailAsync(
EmailAccountConfig config,
string subject,
string htmlBody,
string toName,
string toAddress,
string fromName = "StarBlog"
) {
return await SendEmailAsync(
config,
new MimeMessage {
Subject = subject,
From = {new MailboxAddress(fromName, config.FromAddress)},
To = {new MailboxAddress(toName, toAddress)},
Body = new BodyBuilder {
HtmlBody = htmlBody
}.ToMessageBody()
}
);
} public static async Task<MessageSentEventArgs> SendEmailAsync(EmailAccountConfig config, MimeMessage message,
HttpProxyClient? proxyClient = null) {
MessageSentEventArgs result = null;
using var client = new SmtpClient {
ServerCertificateValidationCallback = (s, c, h, e) => true,
};
if (proxyClient != null) {
client.ProxyClient = proxyClient;
} client.AuthenticationMechanisms.Remove("XOAUTH2");
client.MessageSent += (sender, args) => { result = args; }; await client.ConnectAsync(config.Host, config.Port, SecureSocketOptions.Auto);
await client.AuthenticateAsync(config.FromUsername, config.FromPassword);
await client.SendAsync(message);
await client.DisconnectAsync(true); return result;
}
}

使用比较简单,传入邮箱配置和邮件主题、内容、收件地址就行。

具体的可以接着看下面的代码。

友链申请管理

管理友情链接申请记录的逻辑,同样也是有增删改查,这部分代码跟上面的一样,省略了

构造方法通过依赖注入,从配置系统里读取了邮箱配置,读者可以自行将邮箱配置添加到 appsettings.json 中,这里给出一个outlook邮箱的配置

"EmailAccountConfig": {
"Host": "smtp-mail.outlook.com",
"Port": 587,
"FromUsername": "邮箱地址@outlook.com",
"FromPassword": "邮箱密码",
"FromAddress": "邮箱地址@outlook.com"
}

下面开始是 service 的代码

这里只贴设置是否验证发邮件通知 的代码

public class LinkExchangeService {
private readonly IBaseRepository<LinkExchange> _repo;
private readonly LinkService _linkService;
private readonly EmailAccountConfig _emailAccountConfig; public LinkExchangeService(IBaseRepository<LinkExchange> repo, LinkService linkService, IOptions<EmailAccountConfig> options) {
_repo = repo;
_linkService = linkService;
_emailAccountConfig = options.Value;
} public async Task<LinkExchange?> SetVerifyStatus(int id, bool status, string? reason = null) {
var item = await GetById(id);
if (item == null) return null; item.Verified = status;
item.Reason = reason;
await _repo.UpdateAsync(item); var link = await _linkService.GetByName(item.Name);
if (status) {
await SendEmailOnAccept(item);
if (link == null) {
await _linkService.AddOrUpdate(new Link {
Name = item.Name,
Description = item.Description,
Url = item.Url,
Visible = true
});
}
else {
await _linkService.SetVisibility(link.Id, true);
}
}
else {
await SendEmailOnReject(item);
if (link != null) await _linkService.DeleteById(link.Id);
} return await GetById(id);
} // 本文仅贴上申请通过的代码,其他的也是类似的写法
public async Task SendEmailOnAccept(LinkExchange item) {
const string starblogLink = "<a href=\"https://deali.cn\">StarBlog</a>";
var sb = new StringBuilder();
sb.AppendLine($"<p>您好,友链申请已通过!感谢支持,欢迎互访哦~</p>");
sb.AppendLine($"<br>");
sb.AppendLine($"<p>以下是您申请的友链信息:</p>");
sb.AppendLine($"<p>网站名称:{item.Name}</p>");
sb.AppendLine($"<p>介绍:{item.Description}</p>");
sb.AppendLine($"<p>网址:{item.Url}</p>");
sb.AppendLine($"<p>站长:{item.WebMaster}</p>");
sb.AppendLine($"<p>补充信息:{item.Reason}</p>");
sb.AppendLine($"<br>");
sb.AppendLine($"<br>");
sb.AppendLine($"<br>");
sb.AppendLine($"<p>本消息由 {starblogLink} 自动发送,无需回复。</p>");
await EmailUtils.SendEmailAsync(
_emailAccountConfig,
"[StarBlog]友链申请结果反馈",
sb.ToString(),
item.WebMaster,
item.Email
);
}
}

在设置是否验证的方法中,实现了:

  • 设置一个申请为已验证,自动将该申请的链接添加到友情链接中
  • 设置一个申请为未验证,则自动将对应的友情链接删除(如果存在的话)

其他地方就跟上面的友情链接一样了。

写完之后别忘了注册服务

builder.Services.AddScoped<LinkExchangeService>();
builder.Services.AddScoped<LinkService>();

友链申请

展示功能做完了,还得接着做友链申请的功能,以方便路过的站长申请互换友链~

添加 StarBlog.Web/ViewModels/LinkExchange/LinkExchangeAddViewModel.cs 文件

我们使用 AspNetCore MVC 框架提供的表单验证功能

public class LinkExchangeAddViewModel {
/// <summary>
/// 网站名称
/// </summary>
[Display(Name = "网站名称")]
[Required(ErrorMessage = "必须填写网站名称")]
public string Name { get; set; } /// <summary>
/// 介绍
/// </summary>
[Display(Name = "介绍")]
public string? Description { get; set; } /// <summary>
/// 网址
/// </summary>
[Display(Name = "网址")]
[Required(ErrorMessage = "必须填写网址")]
[DataType(DataType.Url)]
public string Url { get; set; } /// <summary>
/// 站长
/// </summary>
[Display(Name = "站长名称")]
[Required(ErrorMessage = "必须填写站长名称")]
public string WebMaster { get; set; } /// <summary>
/// 联系邮箱
/// </summary>
[Display(Name = "联系邮箱")]
[Required(ErrorMessage = "必须填写联系邮箱")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}

接着写一下页面 StarBlog.Web/Views/LinkExchange/Add.cshtml

@model StarBlog.Web.ViewModels.LinkExchange.LinkExchangeAddViewModel
@{
ViewData["Title"] = "申请友链";
} <div class="container px-4 py-3">
<h2 class="d-flex w-100 justify-content-between pb-2 mb-3 border-bottom">
<div>申请友链</div>
<div>Link Exchange</div>
</h2> <div class="card px-1 py-3">
<form enctype="multipart/form-data" class="card-body row" asp-controller="LinkExchange" asp-action="Add" method="post">
<div class="col-xl-6">
<div class="mb-4">
<h4 class="card-title">友链信息</h4>
<h6 class="card-subtitle mb-3 text-muted">请输入您的网站信息,方便后续联系</h6>
</div> <div class="mb-3">
<label asp-for="Name" class="form-label"></label>
<input asp-for="Name" class="form-control">
<span asp-validation-for="Name" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Description" class="form-label"></label>
<input asp-for="Description" class="form-control">
<span asp-validation-for="Description" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Url" class="form-label"></label>
<input asp-for="Url" class="form-control">
<span asp-validation-for="Url" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="WebMaster" class="form-label"></label>
<input asp-for="WebMaster" class="form-control">
<span asp-validation-for="WebMaster" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control">
<span asp-validation-for="Email" class="form-text text-danger"></span>
</div>
</div> <div class="col-xl-6">
<div class="ms-3 mb-4">
<h4 class="card-title">注意事项</h4>
<h6 class="card-subtitle mb-3 text-muted">申请友情链接需符合以下几点要求</h6>
</div> <ul class="list-group list-group-flush list-group-numbered">
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">相互性</div>
请先在您的网站添加本站链接,再进行友链申请
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">内容类别</div>
本站优先招同类原创、内容相近的博客或网站
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">SEO</div>
Baidu和Google有正常收录,有近期快照的网站优先
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">合法性</div>
不含有违反相关国家法律内容的合法网站,不接受TB客等垃圾站的链接
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">更新及时性</div>
不接受原创内容很少,且长期不更新的网站
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<div class="fw-bold">可访问性</div>
如您的网站无法访问,将会暂时撤销友情链接,如需恢复请联系站长处理
</div>
</li>
</ul>
</div> <div class="form-group">
<button type="submit" class="btn btn-outline-primary">提交</button>
<button type="reset" class="btn btn-outline-warning">重置</button>
</div>
</form>
</div>
</div>

最后是Controller,添加 StarBlog.Web/Controllers/LinkExchangeController.cs 文件

代码如下

public class LinkExchangeController : Controller {
private readonly ILogger<LinkExchangeController> _logger;
private readonly LinkExchangeService _service;
private readonly IMapper _mapper;
private readonly Messages _messages; public LinkExchangeController(
ILogger<LinkExchangeController> logger, LinkExchangeService service, IMapper mapper,
Messages messages) {
_logger = logger;
_service = service;
_mapper = mapper;
_messages = messages;
} [HttpGet]
public IActionResult Add() {
return View();
} [HttpPost]
public async Task<IActionResult> Add(LinkExchangeAddViewModel vm) {
if (!ModelState.IsValid) return View(); if (await _service.HasUrl(vm.Url)) {
_messages.Error("相同网址的友链申请已提交!");
return View();
} var item = _mapper.Map<LinkExchange>(vm);
item = await _service.AddOrUpdate(item); // 发送邮件通知
await _service.SendEmailOnAdd(item); _messages.Info("友链申请已提交,正在处理中,请及时关注邮件通知~");
return RedirectToAction("Index", "Home");
}
}

搞定~

一切就绪

欢迎各位站长大佬来交换友链~!

基于.NetCore开发博客项目 StarBlog - (28) 开发友情链接相关接口的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  2. 基于.NetCore开发博客项目 StarBlog - (22) 开发博客文章相关接口

    前言 本文介绍博客文章相关接口的开发,作为接口开发介绍的第一篇,会写得比较详细,以抛砖引玉,后面的其他接口就粗略带过了,着重于WebApi开发的周边设施. 涉及到的接口:文章CRUD.置顶文章.推荐文 ...

  3. 基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  4. 基于.NetCore开发博客项目 StarBlog - (15) 生成随机尺寸图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  5. 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  6. 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传

    前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了 ...

  8. 基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索

    前言 笔者认为,一个博客网站,最核心的是阅读体验. 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能. 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做 ...

  9. 基于.NetCore开发博客项目 StarBlog - (20) 图片显示优化

    前言 我的服务器带宽比较高,博客部署在上面访问的时候几乎没感觉有加载延迟,就没做图片这块的优化,不过最近有小伙伴说博客的图片加载比较慢,那就来把图片优化完善一下吧~ 目前有两个地方需要完善 图片瀑布流 ...

  10. 基于.NetCore开发博客项目 StarBlog - (21) 开始开发RESTFul接口

    前言 最近电脑坏了,开源项目的进度也受到一些影响 这篇酝酿很久了,作为本系列第二部分(API接口开发)的第一篇,得想一个好的开头,想着想着就鸽了好久,索性不扯那么多了,直接开写吧~ 关于RESTFul ...

随机推荐

  1. 从零开始学习Java系列之Java运行机制与跨平台特性

    全文大约[4000]字,不说废话,只讲可以让你学到技术.明白原理的纯干货!并带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,给你带来具有足够启迪的思考-- 在上一篇文章中,壹哥给大家介绍了J ...

  2. Duplicate File Finder Pro - 重复文件查找器,给你的 Mac 清理出大量磁盘空间

    重复文件查找器 Duplicate File Finder Pro 是一个实用程序,只需3次点击就能在Mac上找到重复的文件.拖放功能和尽可能多的文件夹,你想,然后按下扫描按钮.在一分钟,应用程序将给 ...

  3. 机器学习算法(九): 基于线性判别模型的LDA手写数字分类识别

    1.机器学习算法(九): 基于线性判别模型的LDA手写数字分类识别 1.1 LDA算法简介和应用 线性判别模型(LDA)在模式识别领域(比如人脸识别等图形图像识别领域)中有非常广泛的应用.LDA是一种 ...

  4. 这年头,谁的好友列表还没有躺一个ChatGPT啊?

    你要是说这个,我可不困了 大家好,我最近开始使用一款非常有趣的AI机器人,它叫做ChatGPT.ChatGPT是一款独特的聊天机器人,它可以进行智能对话,回答你的问题,还可以学习你的语言习惯,使得对话 ...

  5. .net core基于HttpClient实现的网络请求库

    Soda.Http 基于HttpClient封装的 Http 请求库.如果有什么好想法,可以提 Issue 或者 Pr.,如果想要使用,直接在nuget搜索Soda.Http即可. Github项目地 ...

  6. python入门教程之四基本语法

    1Python 标识符 在 Python 里,标识符由字母.数字.下划线组成. 在 Python 中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. Python 中的标识符是区分大 ...

  7. 2.自定义@Excel注解实现数据Excel形式导入导出

    前言 这几天在学习如何使用自定义注解实现Excel格式数据导入导出,参考的还是若依框架里面的代码,由于是初学,所以照猫画虎呗,但是难受的是需要复制并根据自己项目修改作者自定义的工具类以及导入这些工具类 ...

  8. 五月十五日java基础知识点

    1.匿名内部类适用于编写事件程序 interface Ishape{ void shape(); } class MyType{ public void outShape(Ishape s){//接口 ...

  9. 【python基础】定时任务框架APScheduler

    1. 基础概念 APScheduler是python中较为简洁直观的定时任务框架,提供了基于固定日期.时间间隔.crontab表达式三种任务类型,并且可以持久化任务(如:将定时任务保存到mysql中) ...

  10. C# 模拟界面点击/UI自动化测试

    有一些UI自动化测试框架,能够实现自动化测试. 本文介绍Peer(微软的TAF技术),也可以实现自动化测试,或是对其他进程进行UI操作.下面是案例~ 在界面上添加俩个按钮: 并处理相应的点击事件: 1 ...