You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中ProfileValidator类里User.Identity.Name返回null的原因?

ASP.NET Core 7中自定义ValidationAttribute无法获取User.Identity.Name的问题解决

问题根源

你在自定义验证特性DisplayNameForCreateAttribute里犯了两个关键错误:

  1. 手动实例化AccessController(new AccessController())——这个实例脱离了ASP.NET Core的请求管道,框架不会为它绑定当前请求的上下文,因此控制器的User属性为空,GetUserName()自然返回null。
  2. 手动创建ApplicationDbContext时传入空的DbContextOptions——这样的DbContext没有正确配置数据库连接,无法正常操作数据。

解决方案

方案一:在验证特性中正确获取请求上下文与DbContext

修改DisplayNameForCreateAttribute,通过ValidationContext获取服务提供者,从而拿到当前请求的HttpContext和配置好的DbContext:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;
using Penfolio2.Data;
using Penfolio2.Models;
using System.ComponentModel.DataAnnotations;

namespace Penfolio2.Validation
{
    public class ProfileValidator
    {
        public class DisplayNameForCreateAttribute : ValidationAttribute
        {
            protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
            {
                var profile = (CreateProfileViewModel)validationContext.ObjectInstance;
                var errors = new List<IdentityError>();

                // 获取服务提供者
                var serviceProvider = validationContext.GetRequiredService<IServiceProvider>();
                
                // 获取当前请求上下文
                var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
                var httpContext = httpContextAccessor.HttpContext;
                
                if (httpContext == null || httpContext.User.Identity?.Name == null)
                {
                    errors.Add(new IdentityError()
                    {
                        Description = "仅注册用户可创建资料,请登录后重试。"
                    });
                }
                else
                {
                    var username = httpContext.User.Identity.Name;
                    // 获取配置好的DbContext实例
                    var db = serviceProvider.GetRequiredService<ApplicationDbContext>();
                    
                    // 示例:检查DisplayName是否已被占用
                    bool displayNameExists = db.PenProfiles.Any(p => p.DisplayName == profile.DisplayName);
                    if (displayNameExists)
                    {
                        errors.Add(new IdentityError()
                        {
                            Description = "该显示名称已被使用。"
                        });
                    }
                }

                if (errors.Count > 0)
                {
                    string errorString = string.Join(" ", errors.Select(e => e.Description));
                    return new ValidationResult(errorString);
                }

                return ValidationResult.Success;
            }
        }
    }
}

同时需要在Program.cs中注册IHttpContextAccessor服务:

builder.Services.AddHttpContextAccessor();

方案二:将验证逻辑移至控制器(服务器端验证)

如果只需要服务器端验证,可移除ViewModel上的验证特性,在控制器POST方法中手动执行验证逻辑,这种方式更直接:

// POST: ProfileController/Create
[Route("Profile/Create")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateProfileViewModel model)
{
    string userId = GetUserId();
    var errors = new List<string>();
    
    // 检查显示名称是否重复
    bool displayNameExists = db.PenProfiles.Any(p => p.DisplayName == model.DisplayName);
    if (displayNameExists)
    {
        errors.Add("该显示名称已被使用。");
    }
    
    // 其他自定义验证逻辑...
    
    // 将错误添加到ModelState
    foreach (var error in errors)
    {
        ModelState.AddModelError(nameof(model.DisplayName), error);
    }
    
    if (ModelState.IsValid)
    {
        // 执行资料创建逻辑
        return RedirectToAction("Index");
    }

    return View(model);
}

额外优化:修正AccessController的DbContext注入

原代码中手动new DbContext的方式会导致连接泄漏等问题,改为构造函数注入:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Penfolio2.Data;

namespace Penfolio2.Controllers
{
    [Authorize]
    public class AccessController : Controller
    {
        private readonly ApplicationDbContext _db;

        public AccessController(ApplicationDbContext db)
        {
            _db = db;
        }

        public string? GetUserName()
        {
            return User?.Identity?.Name;
        }

        protected string GetUserId()
        {
            var user = _db.PenUsers.FirstOrDefault(i => i.NormalizedUserName == User.Identity.Name?.ToUpper());
            if (user == null)
            {
                throw new InvalidOperationException("未找到当前用户。");
            }
            return user.Id;
        }

        protected bool UserHasProfile()
        {
            return _db.PenProfiles.Any(i => i.UserId == GetUserId());
        }
    }
}

对应的ProfileController也要调整构造函数:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Penfolio2.Data;
using Penfolio2.Models;

namespace Penfolio2.Controllers
{
    [Authorize]
    public class ProfileController : AccessController
    {
        private readonly ApplicationDbContext _db;
        private readonly IWebHostEnvironment _environment;

        public ProfileController(ApplicationDbContext db, IWebHostEnvironment environment) : base(db)
        {
            _db = db;
            _environment = environment;
        }

        // 其他方法保持不变
    }
}

内容的提问来源于stack exchange,提问作者Melissa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 05:37:05