繁体   English   中英

ASP.Net Core MVC - Ajax 表单提交,对少数 ViewModel 属性进行 ModelState 验证

[英]ASP.Net Core MVC - Ajax form submission with ModelState validation on few ViewModel properties

问题

我有一个客户端视图模型,其中包含一些必需的属性和非必需的属性。 该视图包含用于更新不同视图模型属性的不同部分。 如果我使用必需的客户端详细信息(例如名字、姓氏、出生日期等)更新视图的一部分,那么我可以将输入类型包装到 ajax 表单中,控制器将从视图模型中获取这些属性并使用 ModelState 进行相应的验证.IsValid 和验证将成功。 但是,如果我在同一视图上有另一个部分需要更新视图模型上的非必需属性(即注释)并从 ajax 表单帖子传递它,那么 ModelState 验证将失败,因为其他必需的属性为空,因为它们从未提交作为ajax表单的一部分。 请注意,必填字段应始终在加载客户端详细信息页面之前填充数据,因此不应为空。

编码

视图模型

public class ClientDetailViewModel
{

    public int ID { get; set; }

    [Required]
    [StringLength(50, MinimumLength = 2)]
    public string FirstName { get; set; }

    [Required]
    [StringLength(50, MinimumLength = 2)]
    public string LastName { get; set; }

    [Required]
    [Display(Name = "Date of Birth")]
    [DataType(DataType.Date)]
    public DateTime DOB { get; set; }

    [Required]
    public string Gender { get; set; }

    public string Notes { get; set; }
}

看法

@model MSIC.Models.ClientViewModels.ClientDetailViewModel
@inject MSIC.Services.Custom.IGenderService GenderService;

<!-- tab-pane for updating core client details -->
<div class="tab-pane active" id="tab_1">
    <form asp-controller="Client" asp-action="Edit" class="form-horizontal">
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="FirstName" class="col-sm-2 control-label"></label>
            <div class="col-sm-10">
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="LastName" class="col-sm-2 control-label"></label>
            <div class="col-sm-10">
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="DOB" class="col-sm-2 control-label"></label>
            <div class="col-sm-10">
                <input asp-for="DOB" class="form-control" />
                <span asp-validation-for="DOB" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Gender" class="col-sm-2 control-label"></label>
            <div class="col-sm-10">
                <select asp-for="Gender" asp-items="@(new SelectList(GenderService.GetAll(),"Code","Name"))" class="form-control">
                </select>
                <span asp-validation-for="Gender" class="text-danger" />
            </div>
        </div>
    </form>
</div>

<!-- different section for updating client notes -->
<div id="divNotes" class="center-block">@Model.Notes</div>
<a href="#" class="btn btn-danger btn-block" data-toggle="modal" data-target="#notesModal" role="button"><b>Edit Notes</b></a>
<form asp-controller="Client" asp-action="EditNotes" class="form-horizontal" data-ajax="true" data-ajax-method="POST" data-ajax-update="#divNotes" data-ajax-mode="replace" data-ajax-success="CloseModal('#notesModal')" data-ajax-failure="AjaxOnFailure(xhr, status, error)">
    <div class="modal fade" id="notesModal" tabindex="-1" role="dialog" aria-labelledby="notesModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
                    <h4 class="modal-title" id="notesModalLabel">edit Reason</h4>
                </div>
                <div class="modal-body">
                    <input type="hidden" asp-for="ID" />
                    <div class="form-group">
                        <div class="col-sm-10">
                            <textarea asp-for="Notes" class="form-control" autofocus></textarea>
                            <span asp-validation-for="Notes" class="text-danger" />
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <div class="text-danger pull-left">
                        <i id="modalErrorIcon" class=""></i>
                        <span id="modalErrorText"></span>
                    </div>
                    <button type="submit" class="btn btn-primary">Save</button>
                    <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
                </div>
            </div>
        </div>
    </div>
</form>

控制器

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditNotes(ClientDetailViewModel model)
{
    //validation fails because required fields in model are null since they were not submitted with this ajax form
    if (ModelState.IsValid)
    {
        //update database with client notes and return notes to screen
        return Content(model.Notes);
    }

    return Content("i haven't coded this yet");
}

问题

  1. 如何在不重复代码的情况下以有效的方式在控制器内部仅对某些字段执行 ModelState 验证? 我搜索了这个问题并且知道像 ModelState[].Errors.Clear(); 这样的选项。 但由于这将是一个大视图,有许多不同的部分可供更新,我想避免在不同的 Action 方法中为我需要执行的所有小 ajax 帖子重复这些语句。 基本上我想使用 ModelState 验证,所以我可以将我的验证逻辑存储在 ViewModel 中,而不是在控制器中分离和/或复制任何验证逻辑。

  2. 我可以看到的另一个选项是将所有必需的属性作为隐藏的输入类型包含在我拥有的每个 ajax 表单中,但这似乎非常不必要,并且肯定会成为维护的噩梦。 是否有更好的方法来为 ajax 帖子传递所有 ViewModel 属性,如果是这样,发送除利用 ModelState.IsValid 检查之外未使用的 ViewModel 属性是否会很昂贵?

  3. 我是 asp.net 的新手,已经开始使用 asp.net core mvc(我很喜欢),并且一直在通过所有教程、SO 问题等进行学习。但是,我是否有可能在错误的处理这个问题如果是这样,使用 asp.net core 和 Microsoft.jQuery.Unobtrusive.Ajax 或其他一些 ajax 工具解决这个问题的正确方法是什么? 请注意,我只发布了这个问题,因为发布的其他类似问题似乎都没有涉及在 View 上共享 ViewModel 属性,但只提交了一些属性,但利用了开箱即用的验证。

提前致谢。

尽管我无法按照建议的方式获得推荐的方法,但我确实使用了有关分离视图模型以进行渲染和提交的建议。 我想我被 Microsoft ASP.NET Core 站点上的电影教程误导了,该教程对详细信息和编辑页面使用相同的电影模型。

为了解决我的问题,我为详细信息页面采用了一个大视图模型,我将使用视图组件来处理我页面中需要更新的各个部分。 这允许我为每个视图组件拥有一个单独的视图模型,并且我的控制器 post 操作只能接受与相应视图组件相关的视图模型,因此它只能执行必要的 ModelState 验证。

这个来自 jQuery ajax 帖子的服务器端验证让我想到了下一个关于将 ModelState 错误返回给客户端进行显示的问题,但我为此提交了一个新问题......

将 ASP.Net Core MVC 中的 BadRequest 返回到 Microsoft jQuery Unobtrusive Ajax 帖子的 ModelState 未定义

将所有调用的所有内容都发回(通过使用隐藏字段)并不昂贵,但它很笨拙,而不是直接的解决方案。

MVC 中的视图模型不必是呈现页面和提交表单的同一个类。 在您的情况下,您可以像这样定义一个用于渲染的视图模型:

public class ClientDetailViewModel {
    public ClientBasicViewModel Basics {get;set;}
    public ClientNotesViewModel Notes {get;set;}
}

然后将字段(姓名、性别等)放入两个新类中的每一个中。 使用两个单独的控制器方法来处理表单提交。 跟踪 FirstName 的发送和接收方式:

  • 渲染时,您将使用 Model.Basics.FirstName 来显示现有值。
  • 对于表单中的字段名称,使其input name="FirstName"...
  • 当提交表单时,控制器方法接受一个 ClientBasicViewModel 类型的参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM