簡體   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