参考
- DeepSeek
- 豆包
- 其他(由于文章是后编写的,相关参考文章未存储连接)
环境
软件/系统 | 版本 | 说明 |
---|---|---|
Windows | windows 10 专业版 22H2 64 位操作系统, 基于 x64 的处理器 | |
Microsoft Visual Studio | Community 2022 (64 位) - Current 版本 17.14.9 | |
Visual Studio Code | 1.102.2 | |
Docker Engine | v28.3.2 | Docker 桌面工具 |
Docker | 28.3.2 | |
pgAdmin4 | 9.0 | PostgreSQL 数据库管理软件 |
PostgreSQL | 15 | |
.NET | 8 | |
ASP.NET Core MVC | ASP.NET Core MVC in .NET 8.0 | |
Microsoft.EntityFrameworkCore.Design | 8.0.18 | nuget 依赖 |
Microsoft.EntityFrameworkCore.Tools | 8.0.18 | nuget 依赖 |
Microsoft.VisualStudio.Web.CodeGeneration.Design | 8.0.7 | nuget 依赖 (Microsoft Visual Studio在通过模型生成控制器与视图时自动安装的依赖) |
Npgsql.EntityFrameworkCore.PostgreSQL | 8.0.11 | nuget 依赖 |
EFCore.NamingConventions | 8.0.3 | nuget 依赖 |
X.PagedList.EF | 10.5.7 | nuget 依赖 |
X.PagedList.Mvc.Core | 10.5.7 | nuget 依赖(安装 X.PagedList.EF 时自动安装) |
本项目入口文件为 Program.cs
,创建项目时为不使用顶级语句
正文
- 创建参数验证注解
AllowedFileExtensionsAttribute.cs
using Humanizer.Localisation; using System.ComponentModel.DataAnnotations;namespace NightMarketPlatformWeb.Validations {/// <summary>/// 自定义验证特性,用于验证文件扩展名是否在允许的列表中。/// https://learn.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-9.0&source=recommendations#custom-attributes/// </summary>public class AllowedFileExtensionsAttribute : ValidationAttribute{private readonly string[] _allowedExtensions;public AllowedFileExtensionsAttribute(string[] allowedExtensions){_allowedExtensions = allowedExtensions;}protected override ValidationResult? IsValid(object? value, ValidationContext validationContext){var file = value as IFormFile;if (file != null){var extension = Path.GetExtension(file.FileName).ToLower();if (!_allowedExtensions.Contains(extension)){// 获取属性的Display名称var displayName = validationContext.DisplayName;// 替换错误消息中的{0}占位符var errorMessage = string.Format(ErrorMessageString, displayName);//return new ValidationResult(errorMessage);}}return ValidationResult.Success;}} }
- 创建接受参数的Dto,在文件属性上面添加上自定义注解
AllowedFileExtensions
,并传入允许的格式[".png", ".jpg", ".jpeg", ".gif"]
(当前示例为:SettingEditRequestDto.cs
)using NightMarketPlatformWeb.Validations; using System.ComponentModel.DataAnnotations;namespace NightMarketPlatformWeb.Dtos.Area {/// <summary>/// 请求DTO类/// </summary>public class SettingEditRequestDto {[Display(Name = "系统名称")][Required(ErrorMessage = "系统名称不能为空")][StringLength(16, MinimumLength = 2, ErrorMessage = " {0} 区间为 {1} - {2}.")]public string SystemName { get; set; } = string.Empty;[Display(Name = "系统描述")][Required(ErrorMessage = "系统描述不能为空")][StringLength(120, MinimumLength = 2, ErrorMessage = " {0} 区间为 {1} - {2}.")]public string SystemDesc { get; set; } = string.Empty;// 留空则不修改[Display(Name = "系统logo文件")]// 验证文件后缀//[FileExtensionsAttribute(Extensions = "png,jpg,jpeg,gif", ErrorMessage = " {0} 格式错误")]// 验证文件后缀,自带 FileExtensionsAttribute 不满足//[FileExtensionsAttribute(ErrorMessage = " {0} 格式错误")][AllowedFileExtensions([".png", ".jpg", ".jpeg", ".gif"], ErrorMessage = " {0} 格式错误")]public IFormFile? SystemLogoFile { get; set; }// 留空则不修改[Display(Name = "系统logo路径")]public string? SystemLogoPath { get; set; }} }
- 视图文件
Index.cshtml
@using NightMarketPlatformWeb.Dtos.Area @model SettingEditRequestDto@{ViewData["Title"] = "设置"; }<h1>列表页</h1><h4>设置</h4> <hr /> <div class="row"><div class="col-md-4"><form asp-action="Index" enctype="multipart/form-data"><div asp-validation-summary="ModelOnly" class="text-danger"></div><div class="form-group"><label asp-for="SystemName" class="control-label"></label><input asp-for="SystemName" class="form-control" /><span asp-validation-for="SystemName" class="text-danger"></span></div><div class="form-group"><label asp-for="SystemDesc" class="control-label"></label><input asp-for="SystemDesc" class="form-control" /><span asp-validation-for="SystemDesc" class="text-danger"></span></div><div class="form-group"><label asp-for="SystemLogoFile" class="control-label"></label><input asp-for="SystemLogoFile" class="form-control" /><span asp-validation-for="SystemLogoFile" class="text-danger"></span><div><img src="@Model.SystemLogoPath" class="img-rounded img-thumbnail mt-2 mb-2" width="150" height="150" /></div></div><div class="form-group"><input type="submit" value="保存" class="btn btn-primary" /></div></form></div> </div>@section Scripts {@{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
- 在控制器内验证、接收、移动文件
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using NightMarketPlatformWeb.Data; using NightMarketPlatformWeb.Dtos.Area; using NightMarketPlatformWeb.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks;namespace NightMarketPlatformWeb.Controllers {// [Authorize(Roles = "Admin")]public class SettingController : Controller{private readonly ApplicationDbContext _context;public readonly IWebHostEnvironment _hostEnvironment;public SettingController(ApplicationDbContext context, IWebHostEnvironment hostEnvironment){_context = context;_hostEnvironment = hostEnvironment;}// GET: Settingpublic async Task<IActionResult> Index(){// 获取所有设置记录var settings = await _context.Settings.ToListAsync();// 初始化DTOvar dto = new SettingEditRequestDto();// 遍历记录并映射到DTO对应的属性foreach (var setting in settings){switch (setting.SettingKey){case "SystemName":dto.SystemName = setting.SettingValue;break;case "SystemDesc":dto.SystemDesc = setting.SettingValue;break;case "SystemLogoPath":dto.SystemLogoPath = setting.SettingValue;break;}}return View(dto);}[HttpPost][ValidateAntiForgeryToken]public async Task<IActionResult> Index(SettingEditRequestDto editRequestDto){// 建议将验证放入筛选器中进行统一处理if (!ModelState.IsValid){return View(editRequestDto);}// 添加事务处理using var transaction = await _context.Database.BeginTransactionAsync();try{// 获取所有设置记录var settings = await _context.Settings.ToListAsync();// 系统名称var systemNameRow = settings.Where(r => r.SettingKey == "SystemName").First();systemNameRow.SettingValue = editRequestDto.SystemName;// 系统设置var systemDescRow = settings.Where(r => r.SettingKey == "SystemDesc").First();systemDescRow.SettingValue = editRequestDto.SystemDesc;// 系统logo 不修改则不更新var systemLogoPathRow = settings.Where(r => r.SettingKey == "SystemLogoPath").First();if (editRequestDto.SystemLogoFile != null){// 文件操作逻辑var newFileName = Path.GetRandomFileName();var fileExt = Path.GetExtension(editRequestDto.SystemLogoFile.FileName);var uploadDir = Path.Combine("temps", DateTime.Now.ToString("yyyy_MM_dd"));var fileDir = Path.Combine(_hostEnvironment.WebRootPath, uploadDir);var filePath = Path.Combine(fileDir, newFileName + fileExt);//// 如果目录不存在则创建if (!Directory.Exists(fileDir)){Directory.CreateDirectory(fileDir);}//using var stream = System.IO.File.Create(filePath);await editRequestDto.SystemLogoFile.CopyToAsync(stream);systemLogoPathRow.SettingValue = Path.Combine(uploadDir, newFileName + fileExt); ;}//await _context.SaveChangesAsync();//await transaction.CommitAsync();}catch (Exception){await transaction.RollbackAsync();}return RedirectToAction(nameof(Index));}} }
预览
- 界面预览