如何创建自定义模型绑定器属性实现自动填充用户信息
问题
我想实现通过属性标记自动填充DTO中的Uploader对象,现有模型如下:
public class AddDocumentDTO { [Required] [CustomAccessTokenAttribute] public required User Uploader {get; set;} [Required] public required int Id {get; set;} [Required] public required IFormFile File {get; set;} }
Uploader需要通过AccessToken中的Claim查询数据库获取,逻辑如下:
public async Task<UserDTO> GetByAccessToken() { var Id = _httpContextAccessor.HttpContext!.User.Claims.Where(claim => claim.Type.ToString() == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").FirstOrDefault()!.Value.ToString(); var userPOCO = await _userRepository.GetAsync(Id); var userDTO = new UserDTO() { InternalUserNumber = userPOCO.INTERNAL_USER_NUMBER, Username = userPOCO.USERNAME!, FirstName = userPOCO.FIRST_NAME!, LastName = userPOCO.LAST_NAME!, EmailAddress = userPOCO.EMAIL_ADDRESS!, WorkGroup = userPOCO.WORK_GROUP!, IsActive = userPOCO.ACTIVE_USER!, IsArmsUser = userPOCO.ARMS_USER, IsSysAdmin = userPOCO.SYSTEM_ADMIN, Avatar = userPOCO.AVATAR }; return userDTO; }
核心需求:
- 用属性标记指示模型绑定自动执行上述逻辑填充
Uploader,且该属性能通用到多个模型 - 确认请求到达模型绑定阶段时,已通过
Authorize过滤器,安全性是否有保障 - 替代目前在仓储/服务层手动传递或填充
User参数的方式,实现自动化
解决方案
1. 实现自定义模型绑定器与标记属性
首先创建一个自定义属性,用于标记需要自动填充的用户类型属性:
[AttributeUsage(AttributeTargets.Property)] public class BindUserFromAccessTokenAttribute : Attribute, IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) { // 匹配需要自动填充的用户类型(这里替换成你的实际UserDTO类型) if (context.Metadata.ModelType == typeof(UserDTO)) { return new BinderTypeModelBinder(typeof(UserFromAccessTokenBinder)); } return null; } }
然后实现对应的模型绑定器,封装用户查询逻辑:
public class UserFromAccessTokenBinder : IModelBinder { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IUserRepository _userRepository; public UserFromAccessTokenBinder(IHttpContextAccessor httpContextAccessor, IUserRepository userRepository) { _httpContextAccessor = httpContextAccessor; _userRepository = userRepository; } public async Task BindModelAsync(ModelBindingContext bindingContext) { // 从Claims中获取用户ID var userIdClaim = _httpContextAccessor.HttpContext?.User.Claims .FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"); if (userIdClaim == null || string.IsNullOrEmpty(userIdClaim.Value)) { bindingContext.ModelState.AddModelError(bindingContext.FieldName, "无法获取用户身份信息"); return; } try { // 查询数据库获取用户信息 var userPOCO = await _userRepository.GetAsync(userIdClaim.Value); if (userPOCO == null) { bindingContext.ModelState.AddModelError(bindingContext.FieldName, "用户不存在"); return; } // 转换为DTO并绑定 var userDTO = new UserDTO() { InternalUserNumber = userPOCO.INTERNAL_USER_NUMBER, Username = userPOCO.USERNAME!, FirstName = userPOCO.FIRST_NAME!, LastName = userPOCO.LAST_NAME!, EmailAddress = userPOCO.EMAIL_ADDRESS!, WorkGroup = userPOCO.WORK_GROUP!, IsActive = userPOCO.ACTIVE_USER!, IsArmsUser = userPOCO.ARMS_USER, IsSysAdmin = userPOCO.SYSTEM_ADMIN, Avatar = userPOCO.AVATAR }; bindingContext.Result = ModelBindingResult.Success(userDTO); } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.FieldName, $"获取用户信息失败:{ex.Message}"); } } }
2. 注册模型绑定器
在Program.cs中注册自定义模型绑定器提供者,确保它被优先使用:
builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new BindUserFromAccessTokenAttribute()); });
3. 在DTO中通用使用属性标记
修改你的DTO,用自定义属性替代原有标记,该属性可直接复用在其他模型的用户属性上:
public class AddDocumentDTO { [Required] [BindUserFromAccessToken] public required UserDTO Uploader { get; set; } [Required] public required int Id { get; set; } [Required] public required IFormFile File { get; set; } }
4. 安全性确认
只要控制器或Action添加了[Authorize]过滤器,请求到达模型绑定阶段前就已经完成身份验证:
- 未携带有效AccessToken的请求会被直接拦截,无法进入模型绑定流程
- 经过验证的请求,
HttpContext.User中的Claims由认证系统生成,是可信的,基于此查询用户安全可靠 - 绑定器中已添加空值校验,避免因Claims缺失导致的异常
5. 简化服务层调用
实现自动填充后,服务层方法无需再手动接收User参数,直接从DTO中获取即可:
public async Task AddDocument(AddDocumentDTO dto) { // 直接使用dto.Uploader,无需手动传递或查询 await _documentRepository.AddAsync(new DocumentPOCO { UploaderId = dto.Uploader.InternalUserNumber, // 其他字段赋值 }); }
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

