ASP.NET Core Web API中如何在自定义验证器中获取路由参数?
解决自定义模型验证器在PUT端点中使用数据库原始日期的问题
要解决PUT端点中需从数据库获取原始StartDate进行验证的问题,我们可以在自定义ValidationAttribute中通过HttpContext获取路由参数,并注入数据库服务查询原始数据,同时区分POST和PUT场景处理比较逻辑,完全符合模型层验证的要求。
实现步骤及代码修改
- 修改自定义验证属性
更新DateGreaterThanAttribute,添加获取路由参数、查询数据库原始数据的逻辑:
using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.Reflection; public class Contract { [JsonPropertyName("StartDate")] public DateTime StartDate { get; set; } [DateGreaterThan("StartDate", ErrorMessage = "EndDate must be greater than start date.")] [JsonPropertyName("EndDate")] public DateTime EndDate { get; set; } } internal class DateGreaterThanAttribute : ValidationAttribute { private readonly string _comparisonProperty; public DateGreaterThanAttribute(string comparisonProperty) { _comparisonProperty = comparisonProperty; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var endDate = (DateTime)value; DateTime startDateToCompare; // 获取HttpContext以读取路由参数 var httpContextAccessor = validationContext.GetService<IHttpContextAccessor>(); var httpContext = httpContextAccessor?.HttpContext; // 判断是否为更新场景(存在路由id) if (httpContext != null && httpContext.Request.RouteValues.TryGetValue("id", out var idObj) && idObj != null) { // 注入DbContext查询数据库中的原始合约数据 var dbContext = validationContext.GetService<YourDbContext>(); if (dbContext == null) { return new ValidationResult("Database context not available."); } // 解析id(根据实际类型调整,比如Guid、long等) if (!int.TryParse(idObj.ToString(), out var contractId)) { return new ValidationResult("Invalid contract ID format."); } // 查询原始合约的StartDate var originalContract = dbContext.Contracts.AsNoTracking().FirstOrDefault(c => c.Id == contractId); if (originalContract == null) { return new ValidationResult("Contract not found in database."); } startDateToCompare = originalContract.StartDate; } else { // 创建场景(POST),使用传入模型中的StartDate var property = validationContext.ObjectType.GetProperty(_comparisonProperty); if (property == null) { throw new ArgumentException($"Comparison property '{_comparisonProperty}' not found on model."); } var comparisonValue = property.GetValue(validationContext.ObjectInstance); if (comparisonValue == null) { return new ValidationResult("StartDate cannot be null."); } startDateToCompare = (DateTime)comparisonValue; } // 执行日期比较逻辑 if (endDate <= startDateToCompare) { return new ValidationResult(ErrorMessage ?? "EndDate must be greater than the start date."); } return ValidationResult.Success; } }
- 注册必要服务
在Program.cs中注册IHttpContextAccessor和你的DbContext,确保验证属性能获取到这些服务:
var builder = WebApplication.CreateBuilder(args); // 注册HttpContext访问器 builder.Services.AddHttpContextAccessor(); // 注册DbContext(根据实际数据库类型调整) builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString"))); // 其他服务注册... var app = builder.Build(); // 中间件配置... app.Run();
关键说明
- 场景区分:通过检查路由中是否存在
id参数,自动区分创建(POST)和更新(PUT)场景。 - 原始数据获取:更新场景下直接从数据库查询原始合约的
StartDate,避免使用PUT请求中可能被篡改的传入值。 - 服务注入:利用
ValidationContext.GetService()方法获取IHttpContextAccessor和DbContext,符合依赖注入原则。 - 错误处理:添加了id解析失败、合约不存在等异常情况的验证错误返回,保证逻辑完整性。
内容的提问来源于stack exchange,提问作者MB22
相关产品推荐
相关产品推荐

