ASP.NET Core MVC中Post模型关联UserId外键时ModelState.IsValid返回false
问题描述
原本项目使用自行搭建的登录/注册系统,后来为接入谷歌、Facebook等第三方授权,集成了Microsoft Identity Platform,登录注册功能正常。但在添加网站帖子时,需要将UserId作为外键记录创建者信息,却出现异常:每次提交帖子时ModelState.IsValid始终返回false,但看不到任何模型验证错误;移除控制器中的IsValid校验后,所有数据(包括UserId)都能正常存入数据库。
Post模型
public class Post { [Key] public int PostId { get; set; } [Required(ErrorMessage ="is required")] [MinLength(3, ErrorMessage ="must be at least 3 characters")] [MaxLength(50, ErrorMessage ="must be 50 characters or less")] public string Title { get; set; } [Required(ErrorMessage ="is required")] [GreaterThan0] [Display(Name ="Players on Team")] public int PlayersOnTeam { get; set; } [Required(ErrorMessage ="is required")] [GreaterThan0] [Display(Name ="Max Players On Team")] public int MaxPlayersOnTeam { get; set; } // 0 = PC, 1 = PS4, 2 = Xbox One, 3 = PS5, 4 = PS3, 5 = Xbox 360 [Required(ErrorMessage ="is required")] public string Platform { get; set; } [Required(ErrorMessage ="is required")] public string Language { get; set; } [Required(ErrorMessage ="is required")] [Display(Name ="Group Type")] public string GroupType { get; set; } [Required(ErrorMessage ="is required")] [GreaterThan0] [Display(Name ="Minimum Level")] public int MinLevel { get; set; } [Required(ErrorMessage ="is required")] [MinLength(10, ErrorMessage ="must be at least 10 characters")] [MaxLength(500, ErrorMessage ="must be 500 characters or less")] public string? Description { get; set; } [Required(ErrorMessage ="is required")] public string GameActivity { get; set; } public DateTime CreatedAt { get; set; } = DateTime.Now; public DateTime UpdatedAt { get; set; } = DateTime.Now; // foreign keys [ForeignKey("UserId")] public AdvanceUser Author { get; set; } // before I had this set up as which is why the controller uses AdvanceUser.Id, not UserId // public string Id { get; set;} // public AdvanceUser Author { get; set; } }
Post控制器创建方法
[HttpPost("/lfg/posts/create")] public IActionResult Create(Post newPost) { if (!loggedIn) { return RedirectToAction("Index", "User"); } if (uid != null) { newPost.Id = uid; } if (ModelState.IsValid == false) { return New(); } var currentUser = _context.Users.FirstOrDefault(u => u.Id == User.FindFirstValue(ClaimTypes.NameIdentifier)); newPost.Author = currentUser; _context.Posts.Add(newPost); _context.SaveChanges(); return Dashboard(); } [HttpPost("/lfg/posts/create")] public async Task<IActionResult> Create(Post newPost) { var user = await _userManager.FindByIdAsync(uid); if (!loggedIn) { return RedirectToAction("Index", "User"); } // if (uid != null) // { // newPost.Id = uid; // } newPost.Author = user; if (ModelState.IsValid == false) { return New(); } _context.Posts.Add(newPost); await _context.SaveChangesAsync(); return Dashboard(); }
控制器中的身份认证属性
private string? uid { get { var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier); return userId; } } private bool loggedIn { get { return User.Identity.IsAuthenticated; } }
新增帖子视图
@model Post <div class="container w-75 mx-auto"> <h2 class="text-center">New Post</h2> <form class="shadow p-3 rounded" asp-controller="Post" asp-action="Create" method="POST"> <div class="mb-3"> <label asp-for="Title"></label> <span asp-validation-for="Title" class="text-danger font-weight-bold"></span> <input class="form-control" asp-for="Title"> </div> <div class="mb-3"> <label asp-for="GameActivity"></label> <span asp-validation-for="GameActivity" class="text-danger font-weight-bold"></span> <select class="form-control" asp-for="GameActivity"> @{ foreach (GameActivity activity in ViewBag.allActivities) { <option value="@activity.Name">@activity.Name</option> } } </select> </div> @* <div class="mb-3"> <label asp-for="GameActivity"></label> <span asp-validation-for="GameActivity" class="text-danger font-weight-bold"></span> <select class="form-control" asp-for="GameActivity"> <option value="Raid">Raid</option> <option value="Public Event">Public Event</option> </select> </div> *@ <div class="mb-3"> <label asp-for="PlayersOnTeam"></label> <span asp-validation-for="PlayersOnTeam" class="text-danger font-weight-bold"></span> <input class="form-control" asp-for="PlayersOnTeam"> </div> <div class="mb-3"> <label asp-for="MaxPlayersOnTeam"></label> <span asp-validation-for="MaxPlayersOnTeam" class="text-danger font-weight-bold"></span> <input class="form-control" asp-for="MaxPlayersOnTeam"> </div> <div class="mb-3"> <label asp-for="Platform"></label> <span asp-validation-for="Platform" class="text-danger font-weight-bold"></span> <select class="form-control" asp-for="Platform"> <option value="PC">PC</option> <option value="PS4">PS4</option> <option value="Xbox">Xbox One</option> <option value="PS5">PS5</option> <option value="PS3">PS3</option> </select> </div> <div class="mb-3"> <label asp-for="Language"></label> <span asp-validation-for="Language" class="text-danger font-weight-bold"></span> <input class="form-control" asp-for="Language"> </div> <div class="mb-3"> <label asp-for="GroupType"></label> <span asp-validation-for="GroupType" class="text-danger font-weight-bold"></span> <select class="form-control" asp-for="GroupType"> <option value="LFG">LFG</option> <option value="LFM">LFM</option> </select> </div> <div class="mb-3"> <label asp-for="MinLevel"></label> <span asp-validation-for="MinLevel" class="text-danger font-weight-bold"></span> <input class="form-control" asp-for="MinLevel"> </div> <div class="mb-3"> <label asp-for="Description"></label> <span asp-validation-for="Description" class="text-danger font-weight-bold"></span> <textarea class="form-control" asp-for="Description"></textarea> </div> <button class="btn btn-success">Submit</button> </form> </div>
Program.cs
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using LFGHub.Data; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Authentication.Certificate; var builder = WebApplication.CreateBuilder(args); // Add services to the container. var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); // builder.Services.AddAuthentication( // CertificateAuthenticationDefaults.AuthenticationScheme) // .AddCertificate(); // builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true) // .AddEntityFrameworkStores<ApplicationDbContext>(); // builder.Services.AddControllersWithViews(); builder.Services.AddIdentity<AdvanceUser, IdentityRole>(options => { options.Password.RequireDigit = true; options.Password.RequireUppercase = true; }).AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders().AddDefaultUI().AddRoles<IdentityRole>(); builder.Services.AddRazorPages(); builder.Services.AddScoped<RoleManager<IdentityRole>>(); builder.Services.AddScoped<UserManager<AdvanceUser>>(); var app = builder.Build(); var serviceProvider = app.Services.GetService<IServiceProvider>(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); // app.UseAuthentication(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.MapRazorPages(); app.Run();
AdvanceUser模型
using Microsoft.AspNetCore.Identity; using LFGHub.Models; public class AdvanceUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } public string DisplayName { get; set; } public string MobileNumber { get; set; } public string DestinyUsername { get; set; } // public virtual ICollection<Post>? Posts { get; set; } public List<Post> Posts { get; set; } = new List<Post>(); }
DbContext
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using LFGHub.Models; namespace LFGHub.Data; public class ApplicationDbContext : IdentityDbContext<AdvanceUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); builder.Entity<Post>() .HasOne(c => c.Author) .WithMany(x => x.Posts); } //public DbSet<Game> Games { get; set; } public DbSet<GameActivity> GameActivities { get; set; } public DbSet<Post> Posts { get; set; } // public DbSet<GroupMember> GroupMembers { get; set; } public DbSet<NewsPost> NewsPosts { get; set; } }
内容的提问来源于stack exchange,提问作者Matthew Arnold
相关产品推荐
相关产品推荐

