using System.Net.Http;
using System.ServiceModel.Syndication;
using System.Xml;
using AngleSharp;
using AngleSharp.Html;
using Dpz.Core.EnumLibrary;
using Dpz.Core.Web.Library.Api;
using Microsoft.AspNetCore.SignalR;
using OpenIddict.Client.AspNetCore;
using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration;
// ReSharper disable UnusedVariable
#pragma warning disable CS0162
namespace Dpz.Core.Web.Controllers;
public class HomeController(
IBingWallpaper bingWallpaper,
IConfiguration configuration,
IFusionCache fusionCache,
IHomeCacheService cacheService
) : Controller
{
/// <summary>
/// home
/// </summary>
/// <returns></returns>
public async Task<IActionResult> Index()
{
ViewBag.Menu = MenuItem.Home;
this.SetTitle("首页");
var model = await cacheService.GetCacheDataAsync();
return View(model);
}
[Route(("news.html"))]
public async Task<IActionResult> News(string mode = "")
{
var news = await cacheService.GetTodayNewsAsync();
if (mode == "content")
{
return Content(
news == null
? ""
: string.Join(
"\n",
(news.NewsList ?? []).Select(x =>
$"{(news.NewsList ?? []).IndexOf(x) + 1}. {x.Title}"
)
)
);
}
return View(news);
}
public async Task<IActionResult> TodayNews()
{
var news = await cacheService.GetTodayNewsAsync();
return PartialView("_TodayNews", news);
}
public async Task<IActionResult> SystemNotifications(
[FromServices] ISystemNotificationHistoryService notificationService
)
{
var notifications = await notificationService.GetRecentAsync();
return PartialView("_SystemNotificationsPartial", notifications);
}
[HttpGet, Route("datetime")]
public async Task<IActionResult> Date()
{
return await Task.FromResult(View());
}
#if DEBUG
public IActionResult Demo(string? msg = null)
{
var s = User;
return Json(new { time = DateTime.Now, message = msg });
}
public IActionResult Demo2()
{
throw new ArgumentException("param is null");
}
public async Task<IActionResult> Demo3(
[FromServices] IHubContext<Dpz.Core.Web.Library.Hub.Notification> hubContext
)
{
var s = new ProgressMessage
{
Message = "测试消息",
Type = MessageType.Success,
ProgressValues = [100m, 100m],
};
await hubContext.Clients.All.SendCoreAsync("hotTopicMessage", [s]);
return View();
}
public IActionResult LayDemo()
{
return View();
}
public async Task<IActionResult> Demo4([FromServices] IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient("edge");
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.dpangzi.com/api/Article");
var response = await httpClient.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
return Content(json, "application/json");
}
#endif
[HttpGet("~/connect/oidc")]
public IActionResult Login(string fromUrl = "/")
{
if (User.Authenticated)
{
return Redirect(string.IsNullOrWhiteSpace(fromUrl) ? "/" : fromUrl);
}
return Challenge(
new AuthenticationProperties { RedirectUri = fromUrl },
OpenIddictClientAspNetCoreDefaults.AuthenticationScheme
);
//return View(model: fromUrl);
}
[Route("markdown-guide.html")]
public async Task<IActionResult> Markdown(
[FromServices] IHttpClientFactory httpClientFactory,
string version = ""
)
{
ViewBag.Menu = MenuItem.Markdown;
this.SetTitle("Markdown 语法示例");
var client = httpClientFactory.CreateClient("edge");
var cdnHost = configuration["S3:CdnHost"];
var markdown = await fusionCache.GetOrSetAsync<string>(
"markdown-guide:content",
async (_, cancellationToken) =>
{
var request = new HttpRequestMessage(
HttpMethod.Get,
$"{cdnHost}/markdown-guide.md"
);
var response = await client.SendAsync(request, cancellationToken);
return await response.Content.ReadAsStringAsync(cancellationToken);
},
options => options.SetDuration(TimeSpan.FromDays(1))
);
return View(model: markdown);
}
[Route("act/{id}")]
public async Task<IActionResult> Activity(
[FromServices] IDynamicPageService dynamicPageService,
string id = ""
)
{
var cache = await fusionCache.TryGetAsync<ActivityModel>(CacheKey.DynamicPageKey(id));
if (cache.HasValue)
{
Response.ContentType = cache.Value.ContentType;
return View(model: cache.Value.Content);
}
var page = await dynamicPageService.FindAsync(id);
if (page == null)
{
return NotFound();
}
var content = page.Content ?? "";
if (page.ContentType == PageContentType.Html)
{
var htmlContext = BrowsingContext.New(Configuration.Default);
var document = await htmlContext.OpenAsync(x => x.Content(content));
if (page.Styles.Count > 0)
{
foreach (var item in page.Styles)
{
var link = document.CreateElement("link");
link.SetAttribute("href", Url.Action("Activity", new { id = item.Name }));
link.SetAttribute("rel", "stylesheet");
document.Head?.Append(link);
}
}
if (page.Scripts.Count > 0)
{
foreach (var item in page.Scripts)
{
var script = document.CreateElement("script");
script.SetAttribute("src", Url.Action("Activity", new { id = item.Name }));
document.Body?.Append(script);
}
}
await using var writer = new StringWriter();
document.ToHtml(writer, new PrettyMarkupFormatter());
content = writer.ToString();
}
var contentType = (page.ContentType ?? PageContentType.Html).GetDescription();
Response.ContentType = contentType;
var cacheValue = new ActivityModel(content, contentType);
await fusionCache.SetAsync(CacheKey.DynamicPageKey(id), cacheValue, TimeSpan.FromDays(30));
return View(model: content);
}
public async Task<IActionResult> GroupBarrages(
[FromServices] IBarrageService barrageService,
string group
)
{
var list = await barrageService.GetGroupBarragesAsync(group);
return Json(list);
}
public async Task<IActionResult> Wallpaper(bool clear = false)
{
if (clear)
{
await bingWallpaper.ClearCacheAsync();
}
var data = await bingWallpaper.GetRandomWallpaperAsync();
return Json(data);
}
public async Task<IActionResult> TodayWallpaper(bool clear = false)
{
if (clear)
{
await bingWallpaper.ClearCacheAsync();
}
var data = await bingWallpaper.GetTodayWallpapersAsync();
return Json(data);
}
[Route("friends.html")]
public async Task<IActionResult> Friends([FromServices] IAppOptionService service)
{
this.SetTitle("友情链接");
ViewBag.Menu = MenuItem.Friends;
var list = await service.GetFriendsAsync();
return View(list);
}
[Route("feed.rss")]
public async Task<IActionResult> Rss([FromServices] IArticleService articleService)
{
var cdnHost = configuration["S3:CdnHost"];
var feed = new SyndicationFeed(
"叫我阿胖",
"追赶大佬脚步的阿胖",
new Uri("https://core.dpangzi.com/feed.rss"),
"feed.rss",
DateTime.Now
)
{
ImageUrl = new Uri($"{cdnHost}/logo.png"),
Copyright = new TextSyndicationContent($"© {DateTime.Now.Year} 叫我阿胖"),
Language = "zh-Hans",
};
var articles = await articleService.GetLatestAsync(50);
feed.Items = articles
.Select(x =>
{
var content = new TextSyndicationContent(x.Introduction);
var addr = Url.Action(
"Read",
"Article",
new { id = x.Id },
HttpContext.Request.Scheme
);
var uri = new Uri(addr!);
return new SyndicationItem(x.Title, content, uri, x.Id, x.CreateTime)
{
Authors = { new SyndicationPerson(x.Author.Name) },
LastUpdatedTime = x.LastUpdateTime,
};
})
.ToList();
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
NewLineHandling = NewLineHandling.Entitize,
NewLineOnAttributes = true,
Indent = true,
Async = true,
};
using var stream = new MemoryStream();
await using var xmlWriter = XmlWriter.Create(stream, settings);
var rssFormatter = new Rss20FeedFormatter(feed, false);
rssFormatter.WriteTo(xmlWriter);
await xmlWriter.FlushAsync();
return File(stream.ToArray(), "text/xml; charset=utf-8"); //application/rss+xml
}
[HttpGet("sitemap")]
public IActionResult Sitemap()
{
return RedirectPermanent("/sitemap.xml");
}
[HttpGet, Route("blacklist.html")]
public async Task<IActionResult> GetBlacklist([FromServices] IBlockService blockService)
{
var blockAccessThreshold = configuration.GetValue("BlockAccessThreshold", 0);
var blocks = await blockService.GetBlocksAsync(blockAccessThreshold);
return View(blocks);
}
[HttpGet, Route("dialog-demo.html")]
public IActionResult DialogDemo()
{
this.SetTitle("Dialog 测试演示");
ViewBag.Menu = MenuItem.Home;
return View();
}
[HttpGet, Route("robots.txt")]
public async Task<IActionResult> Robots([FromServices] IAppOptionService service)
{
var content = await service.GetRobotsAsync();
return Content(content, "text/plain; charset=utf-8");
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这段代码是一个 ASP.NET Core MVC 控制器(HomeController),属于名为 Dpz.Core 的项目。控制器主要负责处理网站首页及若干通用页面/接口的请求,代码如下:
整体结构
- 命名空间:
Dpz.Core.Web.Controllers - 继承自:
Controller - 使用了 C# 12 主构造函数,直接注入以下依赖:
IBingWallpaper:必应壁纸服务IConfiguration:配置对象IFusionCache:缓存框架(FusionCache)IHomeCacheService:首页缓存服务
- 这些依赖可在控制器内直接使用,减少了冗余的字段声明。
主要 Action 方法
1. 首页 Index
- 设置菜单为“首页”,页面标题为“首页”。
- 调用
cacheService.GetCacheDataAsync()获取首页缓存数据,并返回对应视图。
2. 新闻页 News
- 路由:
news.html - 可选参数
mode,默认空字符串。 - 获取今日新闻缓存数据
GetTodayNewsAsync()。 - 若
mode == "content",则只返回纯文本,将新闻列表中的标题按1.xxx\n2.xxx格式用换行拼接,用于某些外部调用。 - 否则返回新闻视图。
3. 今日新闻局部视图 TodayNews
- 获取今日新闻并返回局部视图
_TodayNews。
4. 系统通知 SystemNotifications
- 通过
[FromServices]注入ISystemNotificationHistoryService,获取最近通知记录,返回局部视图_SystemNotificationsPartial。
5. 日期时间页 Date
- 路由:
GET datetime - 异步返回视图,可能用于展示当前时间(模板中实现细节未给出)。
调试专用 Action(仅在 DEBUG 编译下存在)
被 #if DEBUG 包裹,用于开发/测试:
Demo
- 接收可选参数
msg,返回 JSON(当前时间和传入的消息)。
Demo2
- 直接抛出异常,用于测试全局异常处理。
Demo3
- 注入 SignalR Hub 上下文
IHubContext<Notification>,构造一个ProgressMessage对象,通过SendCoreAsync("hotTopicMessage", ...)向所有客户端推送消息,然后返回视图。
LayDemo
- 返回视图,可能是测试 Layui 或其他前端组件。
Demo4
- 注入
IHttpClientFactory,通过名为edge的 HTTP 客户端请求https://api.dpangzi.com/api/Article,将响应原文作为application/json返回。
身份认证与登录
Login
- 路由:
~/connect/oidc(注意路由写法~/表示根目录) - 参数
fromUrl用于登录成功后跳转。 - 若用户已认证,直接重定向到
fromUrl或首页。 - 否则使用
Challenge方法发起认证质询,认证方案为OpenIddictClientAspNetCoreDefaults.AuthenticationScheme(即 OpenIddict 客户端),用于第三方/OIDC 登录。
Markdown 指南页
Markdown
- 路由:
markdown-guide.html - 注入
IHttpClientFactory来获取 HTTP 客户端。 - 从配置
S3:CdnHost读取 CDN 地址。 - 使用
fusionCache.GetOrSetAsync缓存 Markdown 内容(缓存键"markdown-guide:content",缓存一天)。 - 缓存 miss 时,从
{cdnHost}/markdown-guide.md下载 Markdown 文本。 - 最终将文本作为 model 渲染到视图。
动态页面
Activity
- 路由:
act/{id} - 注入
IDynamicPageService,根据id获取动态页面内容。 - 首先尝试从 FusionCache 获取对应缓存(键为
CacheKey.DynamicPageKey(id)),若命中,则直接设置响应 ContentType 并返回 View。 - 缓存 miss 时调用服务查找页面:
- 未找到则返回 404。
- 如果页面内容类型是 HTML:
- 使用 AngleSharp 解析 HTML 字符串。
- 为页面中追加 CSS 样式链接:遍历
page.Styles,每个样式项的链接指向Url.Action("Activity", new { id = item.Name })(即同样通过动态页路由获取资源)。 - 为页面追加脚本标签:遍历
page.Scripts,脚本地址同样指向动态页接口。 - 通过
PrettyMarkupFormatter格式化 HTML 并输出字符串。
- 根据内容类型(HTML 或其它)获取描述,设置
Response.ContentType。 - 将内容包裹成
ActivityModel缓存,有效期 30 天。 - 返回 View,model 为 HTML 内容。
这个方法实现了 动态 HTML 页面/样式/脚本 的统一管理和渲染。
弹幕相关
GroupBarrages
- 注入
IBarrageService,参数group,获取指定分组的弹幕列表,返回 JSON。
壁纸相关(必应壁纸)
Wallpaper
- 参数
clear:若为 true 则先清除壁纸缓存。 - 调用
bingWallpaper.GetRandomWallpaperAsync()获取随机壁纸,返回 JSON。
TodayWallpaper
- 同样支持 clear,调用
bingWallpaper.GetTodayWallpapersAsync()获取今日壁纸列表,返回 JSON。
友情链接
Friends
- 路由:
friends.html - 注入
IAppOptionService - 设置标题“友情链接”和菜单项。
- 调用
service.GetFriendsAsync()获取列表并返回视图。
RSS 订阅
Rss
- 路由:
feed.rss - 注入
IArticleService - 从配置获取 CDN Host,创建
SyndicationFeed对象(feed 标题、描述、链接、ID、时间、图片、版权、语言)。 - 调用
articleService.GetLatestAsync(50)获取最新 50 篇文章。 - 每篇文章生成一个
SyndicationItem:- 标题、摘要内容、文章链接(通过
Url.Action生成)。 - 作者、更新时间。
- 标题、摘要内容、文章链接(通过
- 使用
Rss20FeedFormatter和XmlWriter将 feed 写入内存流,最后以text/xml; charset=utf-8返回文件流,即生成 RSS 2.0 订阅源。
站点地图与机器人协议
Sitemap
- 路由:
sitemap,永久重定向到/sitemap.xml。
Robots
- 路由:
robots.txt - 注入
IAppOptionService,调用GetRobotsAsync()获取 robots 内容,以纯文本返回。
黑名单页面
GetBlacklist
- 路由:
blacklist.html - 注入
IBlockService - 从配置读取
BlockAccessThreshold(默认 0),表示封禁访问次数的阈值。 - 调用
blockService.GetBlocksAsync(threshold)获取黑名单列表,返回视图。
对话框演示页
DialogDemo
- 路由:
dialog-demo.html - 设置标题和菜单,返回视图。
辅助扩展说明
SetTitle与ViewBag.Menu:用于设置视图的标题与高亮菜单。this.SetTitle(...)可能是一个扩展方法,根据控制器上下文设置页面标题。- 代码中使用了多个第三方/业务库:如 AngleSharp(HTML解析)、FusionCache(缓存)、SignalR(实时通信)、OpenIddict(OIDC客户端)等。
// ReSharper disable UnusedVariable和#pragma warning disable CS0162用于抑制编译/分析警告,说明存在一些未使用的变量或不可达代码(如Demo中的var s = User;只赋值未使用)。
总结
HomeController 是一个多功能控制器,承担了:
- 首页 / 新闻 / 动态页渲染
- 必应壁纸、弹幕、系统通知等接口
- RSS 生成、Robots、Sitemap 等 SEO 相关功能
- 登录(OIDC)、黑名单管理、工具演示页面等
整个类大量依赖缓存(FusionCache+自定义缓存服务)提升性能,通过依赖注入解耦业务服务,并使用了 ASP.NET Core 的 MVC 特性(Razor 视图、PartialView、Json、Content、File 等返回类型)。
AI 正在分析代码…
评论加载中...