using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Dpz.Core.Public.ViewModel.Request;
using Dpz.Core.Public.ViewModel.Request.HandlerRequest;
using Dpz.Core.Public.ViewModel.RequestEvent;
using Dpz.Core.Service.ObjectStorage.Services;
using Dpz.Core.Web.Library.Api;
namespace Dpz.Core.Web.Controllers;
public partial class ArticleController(
IArticleService articleService,
IObjectStorageOperation objectStorageService,
IMediator mediator,
ILogger<ArticleController> logger
) : Controller
{
public async Task<IActionResult> Index(
[FromServices] IHomeCacheService cacheService,
string? tag = "",
[Range(10, 100)] int pageSize = 10,
int pageIndex = 1,
CancellationToken cancellationToken = default
)
{
if (!ModelState.IsValid)
{
return NotFound();
}
var tags = string.IsNullOrEmpty(tag) ? [] : new[] { tag };
this.SetTitle(string.IsNullOrEmpty(tag) ? "文章列表" : $"标签-{tag}-文章列表");
var list = await articleService.GetPagesAsync(
pageIndex,
pageSize,
tags: tags,
cancellationToken: cancellationToken
);
if (Request.Headers["X-PJAX"] == "true" && Request.Query["_pjax"] == "#article-list")
{
return PartialView("_ArticleListPartial", list);
}
var model = new ArticleIndexModel
{
LikeArticle = await cacheService.GetRandomArticlesAsync(cancellationToken),
List = list,
News = await cacheService.GetLatestArticlesAsync(cancellationToken),
Tags = await cacheService.GetArticleTagsAsync(cancellationToken),
};
return View(model);
}
public async Task<IActionResult> Read(
string id = "",
string text = "",
CancellationToken cancellationToken = default
)
{
var article = await mediator.Send(
new ArticleReadRequest { Id = id, Text = text },
cancellationToken
);
if (article == null)
{
return NotFound();
}
this.SetTitle(article.Title);
var pageMetaPage = new VmPageMetadata
{
Description = ClearIntroductionRegex().Replace(article.Introduction ?? "", ""),
Keywords = [article.Title],
Relations = ["Article", "Read", id],
};
ViewData["PageMetadata"] = pageMetaPage;
ViewData["Text"] = text;
return View(article);
}
[CheckAuthorize]
[HttpPost]
public async Task<IActionResult> Upload(CancellationToken cancellationToken)
{
var file = Request.Form.Files.FirstOrDefault();
if (file is { Length: > 0 } && file.ContentType.Contains("image"))
{
//var userInfo = User.GetIdentity();
var path = new[] { "images", "article", DateTime.Now.ToString("yyyy-MM-dd") };
var fileName =
ObjectId.GenerateNewId() + file.FileName[file.FileName.LastIndexOf('.')..];
var result = await objectStorageService.UploadAsync(
file.OpenReadStream(),
path,
fileName,
cancellationToken
);
return Json(
new
{
success = 1,
message = "",
url = result.AccessUrl,
}
);
}
return Json(
new
{
success = 0,
message = "请选择一张图片!",
url = "",
}
);
}
[HttpPost, CheckAuthorize]
public async Task<IActionResult> Publish(
EditArticleRequest model,
string newTag = "",
CancellationToken cancellationToken = default
)
{
var userInfo = User.RequiredUserInfo;
var checkResult = ModelState.CheckModelState();
if (checkResult.IsValid)
{
return Json(new ResultInfo(string.Join("\n", checkResult.ErrorMessages)));
}
if (!string.IsNullOrWhiteSpace(newTag))
{
var newTags = newTag.Split(',').Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
model.Tags.AddRange(newTags);
}
// 编辑
if (!string.IsNullOrEmpty(model.Id))
{
var article = await articleService.GetArticleAsync(model.Id, cancellationToken);
if (article == null)
{
return Json(new ResultInfo("文章不存在"));
}
if (userInfo.Id != article.Author?.Id)
{
return Json(new ResultInfo("不能修改别人发布的文章!"));
}
await articleService.EditArticleAsync(model, userInfo, cancellationToken);
return Json(new ResultInfo(data: model.Id));
}
try
{
// 新增
var createdArticle = await articleService.CreateArticleAsync(
model,
userInfo,
cancellationToken
);
await articleService.ClearCacheAsync(cancellationToken);
return Json(new ResultInfo(data: createdArticle.Id));
}
catch (Exception ex)
{
logger.LogInformation(ex, "创建文章失败");
return Json(ResultInfo.ToFail(ex.Message));
}
}
[CheckAuthorize, HttpPost]
public async Task<IActionResult> Delete(string id, CancellationToken cancellationToken)
{
var article = await articleService.GetArticleAsync(id, cancellationToken);
if (article == null || User.RequiredUserId != article.Author?.Id)
{
return Json(new ResultInfo("未找到文章,或者不能删除别人发布的文章!"));
}
await articleService.DeleteAsync(id, cancellationToken);
return Json(new ResultInfo(true));
}
[HttpGet]
public async Task<IActionResult> Search(
string? keyword = "",
CancellationToken cancellationToken = default
)
{
var request = new ArticleSearchRequest { Keyword = keyword };
var searchResult = await mediator.Send(request, cancellationToken);
var tags = await articleService.GetAllTagsAsync(cancellationToken);
var model = new ArticleSearchModel(keyword, searchResult, tags);
this.SetTitle($"搜索 - {keyword}");
return View(model);
}
[GeneratedRegex("<[^>]*>")]
private static partial Regex ClearIntroductionRegex();
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个ASP.NET Core MVC控制器,专门处理文章相关的功能。让我详细解释一下这个代码的结构和功能:
控制器概述
ArticleController 是一个部分类(partial class),使用了依赖注入来获取所需的服务:
IArticleService- 文章业务服务IObjectStorageOperation- 对象存储服务IMediator- 中介者模式实现ILogger<ArticleController>- 日志服务
主要功能方法
1. Index 方法 - 文章列表页
public async Task<IActionResult> Index(...)
- 支持按标签筛选文章
- 分页显示(页大小10-100,默认10)
- 支持PJAX异步加载(检查请求头判断是否返回部分视图)
- 返回包含推荐文章、最新文章、标签等完整页面数据
2. Read 方法 - 文章详情页
public async Task<IActionResult> Read(...)
- 通过ID和文本参数获取文章内容
- 使用中介者模式发送请求
- 设置页面元数据(描述、关键词等)
- 使用正则表达式清理文章介绍中的HTML标签
3. Upload 方法 - 图片上传
[CheckAuthorize]
[HttpPost]
public async Task<IActionResult> Upload(...)
- 需要授权验证
- 只接受图片文件
- 按日期组织存储路径
- 生成唯一文件名(使用ObjectId)
- 返回JSON格式的上传结果
4. Publish 方法 - 发布/编辑文章
[HttpPost, CheckAuthorize]
public async Task<IActionResult> Publish(...)
- 需要授权验证
- 支持新增标签功能
- 区分新增和编辑操作
- 验证用户权限(只能编辑自己的文章)
- 包含完整的错误处理
5. Delete 方法 - 删除文章
[CheckAuthorize, HttpPost]
public async Task<IActionResult> Delete(...)
- 需要授权验证
- 验证文章存在性和用户权限
- 只允许删除自己发布的文章
6. Search 方法 - 文章搜索
[HttpGet]
public async Task<IActionResult> Search(...)
- 支持关键词搜索
- 返回搜索结果和所有标签
- 设置搜索页面标题
技术特点
- 异步编程:所有方法都使用
async/await模式 - 依赖注入:通过构造函数注入所需服务
- 模型验证:使用数据注解进行参数验证
- 权限控制:使用
[CheckAuthorize]特性进行授权验证 - PJAX支持:支持异步页面加载技术
- 正则表达式:使用源生成器优化正则表达式性能
- 错误处理:完善的异常处理和用户友好的错误信息
- RESTful设计:遵循REST API设计原则
这个控制器实现了一个完整的文章管理系统,包括浏览、搜索、发布、编辑、删除等核心功能,代码结构清晰,功能完善。
评论加载中...