【问题标题】:.net core 2.1 validation state: invalid.net core 2.1 验证状态:无效
【发布时间】:2018-09-18 11:02:36
【问题描述】:

以下简单的 .NET Core 2.1 MVC 代码在我提交创建时报告“验证状态:无效”。没有 Owner 财产一切正常;如果不需要 Owner 属性,它就可以工作。

所有者是服务器端上下文中的当前用户,不应从客户端提交,因此 Create.cshtml 在表单中没有所有者输入。

错误:

info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method AnnouncementApp.Controllers.AnnouncementsController.Create (AnnouncementApp) with arguments (AnnouncementApp.Models.Announcement) - Validation state: Invalid

型号:

using System;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using AnnouncementApp.Models.Attributes;
using Microsoft.AspNetCore.Identity;
//using System.Security.Claims;

namespace AnnouncementApp.Models
{
    public class Announcement
    {   
        public int ID { get; set; }
        [Required]
        public string Content { get; set; }
        [Display(Name = "Start Date and Time")]
        public DateTime StartDate { get; set; }

        [StartEndDate("End Date and Time must be after Start Date and Time")]
        [Display(Name = "End Date and Time")]
        public DateTime EndDate { get; set; }

        [Required]
        [BindNever]
        public IdentityUser Owner { get; set; }
    }   
}

控制器方法:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("ID,Content,StartDate,EndDate")] Announcement announcement)
    {
        if (ModelState.IsValid)
        {
            var user = await _userManager.GetUserAsync(this.User);
            announcement.Owner = user;
            _context.Add(announcement);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(announcement);
    }

Create.cshtml

@model AnnouncementApp.Models.Announcement

@{
    ViewData["Title"] = "Create";
}

<h2>Create</h2>

<h4>Announcement</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Content" class="control-label"></label>
                <textarea asp-for="Content" class="form-control"></textarea>
                <span asp-validation-for="Content" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="StartDate" class="control-label"></label>
                <input asp-for="StartDate" class="form-control" />
                <span asp-validation-for="StartDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="EndDate" class="control-label"></label>
                <input asp-for="EndDate" class="form-control" />
                <span asp-validation-for="EndDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

【问题讨论】:

  • ID 需要隐藏字段吗?
  • 不,我不知道。我认为Required 和BindNever 只是冲突。必需的似乎优先。我想将其设置为必需,因为我希望实体框架脚手架使 CREATE TABLE sql 在结果 OwnerId 列上具有 NOT NULL;但这会破坏模型绑定验证。
  • int 应转换为 NOT NULL。诠释? (或可为空的数据类型)转换为 NULL。但是您可能需要考虑创建一组与您的域模型分开的 ViewModel。域模型可以关联到数据库,而 ViewModel 可以关联到网页。
  • @sean 是正确的。为什么要包含您永远不想从客户端传递的东西作为客户端验证?仅使用视图通过任何测试和验证输入所需的内容创建 ViewModel。
  • @sean 是正确的。我应该将 ViewModel 从域模型中分离出来。谢谢。

标签: .net-core asp.net-core-mvc


【解决方案1】:

对于Announcement,它将为客户端验证和数据库表应用[Required]

正如 cmets 所示,您可以考虑将 Announcement 拆分为 Db 模型和 ViewModel,您可以定义一个新的 AnnouncementViewModel 用于客户端验证。

对于另一个选项,您可以尝试在 fluent api 中配置 [Required] 而不是属性。

以下是详细步骤。

  1. 更改Announcement

    public class Announcement
    {
    public int ID { get; set; }
    [Required]
    public string Content { get; set; }
    [Display(Name = "Start Date and Time")]
    public DateTime StartDate { get; set; }
    public string OwnerId { get; set; }
    //[Required]
    [BindNever]
    [ForeignKey("OwnerId")]
    public IdentityUser Owner { get; set; }
    }
    
  2. ApplicationDbContext 中流利的 api

        protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    
        builder.Entity<Announcement>()
               .Property(a => a.OwnerId)
               .IsRequired();           
    }
    
  3. 控制器

        [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("ID,Content,StartDate")] Announcement announcement)
    {
        if (ModelState.IsValid)
        {
            var user = await _userManager.GetUserAsync(User);
            announcement.Owner = user;
            _context.Add(announcement);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(announcement);
    }
    

【讨论】:

  • Fluent API 可能是矫枉过正。 ViewModel 是一个干净的解决方案。
【解决方案2】:

我不是 100% 确定,您定义的问题是什么,但是如果您想抑制“模型无效”错误,因为您总是通过 HttpContext 设置 Owner 属性,您可以在验证之前使用以下内容型号:

ModelState["Owner"].ValidationState = ModelValidationState.Valid

我认为您的问题是您告诉路由器永远不要绑定“所有者”,但您仍然告诉它是必需的,因此 ModelState 可能会使它无效。

只要使用了“Required”注解,我认为如果没有正确设置 ModelState 就不会验证。

例子:

ModelState["Owner"].ValidationState = ModelValidationState.Valid
if (ModelState.IsValid)
        {
            var user = await _userManager.GetUserAsync(this.User);
            announcement.Owner = user;
            _context.Add(announcement);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(announcement);

【讨论】:

  • 强制状态有效可能不是一个好习惯。我只是将 ViewModel 从 Domain Model 中拆分出来
猜你喜欢
  • 1970-01-01
  • 2021-11-06
  • 2019-10-13
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多