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

ASP.NET Core MVC模态框验证异常:自定义验证触发后跳转无格式页面

ASP.NET Core MVC 模态弹窗自定义验证跳转问题

开发小型ASP.NET Core MVC应用时遇到数据验证异常:通过模态弹窗录入数据,模型自带的必填、长度校验可正常在弹窗内显示错误提示;但执行重复数据的自定义验证时,页面会跳转到无格式的非模态页面,无法在弹窗内展示错误信息。


模型类

[Key]
public int IDArbitro { get; set; }

[Column(TypeName = "varchar(50)")]
[Display(Name = "Apellidos")]
[Required(ErrorMessage = "Los apellidos son obligatorios")]
[StringLength(50, ErrorMessage = "El campo {0} no debe exceder de {1} caracteres.")]
public string Apellidos { get; set; }

[Column(TypeName = "varchar(20)")]
[Display(Name = "Nombre")]
[Required(ErrorMessage = "El nombre es obligatorio")]
[StringLength(50, ErrorMessage = "El campo {0} no debe exceder de {1} caracteres.")]
public string Nombre { get; set; }

控制器

[HttpGet]
public IActionResult InsertOrUpdate(int id)
{
    Arbitro arbitro = new Arbitro();

    if (id > 0)
    {
        arbitro = _context.Arbitros.Where(x => x.IDArbitro == id).SingleOrDefault();
    }

    return PartialView("InsertOrUpdate", arbitro);
}

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult InsertOrUpdate(Arbitro arbitro)
{
    if (ModelState.IsValid)
    {
        if (ValidarDatos(arbitro))
        {
            if (arbitro.IDArbitro == 0)
            {
                _context.Arbitros.Add(arbitro);
            }
            else
            {
                _context.Arbitros.Update(arbitro);
            }                    

            _context.SaveChanges();

            return RedirectToAction("List", "Arbitro");
        }
        else
        {
            return PartialView("InsertOrUpdate", arbitro);
        }
    }
    else
    {
        return PartialView("InsertOrUpdate", arbitro);
    }
}

private bool ValidarDatos(Arbitro arbitro)
{
    bool isOK = true;

    if (_context.Arbitros.Any(x => x.Nombre == arbitro.Nombre 
                              && x.Apellidos == arbitro.Apellidos 
                              && x.IDArbitro != arbitro.IDArbitro))
    {
        ModelState.AddModelError(string.Empty, $"Ya existe un arbitro con el nombre {arbitro.Nombre} y los apellidos {arbitro.Apellidos}");

        isOK = false;
    }

    return isOK;
}

视图

@model FootballStats.Models.Arbitro
@{ 
    ViewData["Title"] = "Modificar";
}

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

<div class="modal fade" id="insertOrUpdateModal" tabindex="-1" role="dialog" aria-labelledby="insertOrUpdateModalLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">

            <div class="modal-header bg-dark">
                @if (@Model.IDArbitro == 0)
                {
                    <h3 class="text-white"><strong>Añadir árbitro</strong></h3>
                }
                else
                {
                    <h3 class="text-white"><strong>Modificar árbitro</strong></h3>
                }
            </div>

            <form asp-controller="Arbitro" asp-action="InsertOrUpdate" data-ajax-method="POST">
                <div class="modal-body">
                    <input type="hidden" asp-for="IDArbitro" />

                    <div class="mb-2">
                        <label class="form-label">Nombre</label>
                        <input class="form-control form-control sm" asp-for="Nombre" type="text" />
                        <!--<span asp-validation-for="Nombre" class="text-danger"></span>
                        <p style="color:#FF0000" ;>@Html.ValidationMessageFor(m => m.Nombre)</p>-->
                    </div>

                    <div class="mb-2">
                        <label class="form-label">Apellidos</label>
                        <input class="form-control form-control sm" asp-for="Apellidos" type="text" />
                    </div>
                    
                    @Html.ValidationSummary(false, "", new { @class = "text-danger" })
                </div>

                <div class="modal-footer">
                    <button class="btn btn-primary" type="submit">Guardar</button>
                    <button type="button" class="btn btn-dark" data-bs-dismiss="modal">Cancelar</button>                                        
                </div>
            </form>
        </div>
    </div>
</div>

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

问题根源

表单仅添加了data-ajax-method="POST"但未启用Unobtrusive AJAX提交机制,导致:

  1. 客户端验证(模型特性校验)通过jQuery Validation正常工作,无需提交到服务器
  2. 服务器端自定义验证失败时,表单以普通提交方式返回PartialView,浏览器直接渲染该局部视图,出现无格式页面

解决方案

1. 安装Unobtrusive AJAX包

通过NuGet安装Microsoft.jQuery.Unobtrusive.Ajax:

Install-Package Microsoft.jQuery.Unobtrusive.Ajax

2. 引入Unobtrusive AJAX脚本

在视图中添加脚本引用(放在jQuery和Validation脚本之后):

<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js"></script>

3. 修改表单AJAX配置

更新表单属性,启用AJAX提交并指定内容替换目标:

<form asp-controller="Arbitro" asp-action="InsertOrUpdate" 
      data-ajax="true" 
      data-ajax-method="POST"
      data-ajax-update="#insertOrUpdateModal .modal-content"
      data-ajax-success="function() { 
          if ($('#insertOrUpdateModal .text-danger').length === 0) { 
              $('#insertOrUpdateModal').modal('hide'); 
              location.reload(); 
          } 
      }">
  • data-ajax="true":启用Unobtrusive AJAX提交
  • data-ajax-update:指定服务器返回的PartialView替换弹窗内的.modal-content区域,确保错误信息留在弹窗中
  • data-ajax-success:验证通过时关闭弹窗并刷新列表页面

4. 完善验证消息显示

恢复输入框的验证消息标签,确保客户端验证提示正常显示:

<div class="mb-2">
    <label class="form-label">Nombre</label>
    <input class="form-control form-control-sm" asp-for="Nombre" type="text" />
    <span asp-validation-for="Nombre" class="text-danger"></span>
</div>

<div class="mb-2">
    <label class="form-label">Apellidos</label>
    <input class="form-control form-control-sm" asp-for="Apellidos" type="text" />
    <span asp-validation-for="Apellidos" class="text-danger"></span>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:42:02