您现在的位置是:首页 >技术教程 >第14章后端验证中间件使用场景与选择网站首页技术教程

第14章后端验证中间件使用场景与选择

zhoujian_911 2024-06-17 11:27:01
简介第14章后端验证中间件使用场景与选择

.Net框架前端输入的后端常用验证中间件有DataAnnotations、FluentValidation两种可供选择使用。

FluentValidation

    解耦了模型类定义与验证定义。

    解耦了验证实现与控制器类的定义。

    可以通过泛型方式方法和具体为特定的模型类的验证实现进行定义,有利于验证的统一定义和管理。

    可以泛型方式把所有的验证实现类的实例进行注入例如:

var typeFinder = Singleton<ITypeFinder>.Instance;

var consumers1 = typeFinder.FindClassesOfType(typeof(IValidator<>)).ToList();

foreach (var consumer in consumers1)

    foreach (var findInterface in consumer.FindInterfaces((type, criteria) =>

    {

        var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());

        return isMatch;

    }, typeof(IValidator<>)))

        builder.Services.AddScoped(findInterface, consumer);

    异步定义实现,不支持自动验证。

    学习和使用成本都很高,如非必须不管理是Web Mvc 还是Web Api 都建议使用

    其是第3方验证中间件,如果前端是Razor页面那么就不要考虑使用

DataAnnotations

    验证的定义实现与模型类和控制器类的定义实现紧密耦合。

    其是内置中间件,如果前端是Razor页面,那么就是它了。

    与Razor页面配合能够足够简单的完成自动验证。

自动验证:

  1. 在输入完成并失去焦点后,立即渲染显示验证信息。
  2. 在单击提交按钮后,便未跳转到指定的控制器行为方法,渲染显示验证信息。

手动验证:

    单击提交按钮后,跳转到指定的控制器行为方法中,并在该方法中进行验证,如果未通过验证,那么在前端

渲染显示验证信息例如:

if (_customerSettings.UsernamesEnabled && await _customerService.GetCustomerByUsernameAsync(request.Username) != null)

            {

                result.AddError(await _localizationService.GetResourceAsync("Account.Register.Errors.UsernameAlreadyExists"));

                return result;

            }

  if (!string.IsNullOrWhiteSpace(model.Email) && await _customerService.GetCustomerByEmailAsync(model.Email) != null)

                ModelState.AddModelError(string.Empty, "Email is already registered");

if (ModelState.IsValid)

1 Web.Areas.Admin.Models.Customers.RoleModel

using Microsoft.AspNetCore.Mvc;

using System.ComponentModel.DataAnnotations;

namespace Web.Areas.Admin.Models.Customers

{

    /// <summary>

    /// 【角色模型--纪录】

    /// <remarks>

    /// 摘要:

    ///     为角色添加/编辑Razor页面输入和验证的渲染显示提供数据支撑。

    /// </remarks>

    /// </summary>

    public record RoleModel

    {

        /// <summary>

        /// 【编号】

        /// <remarks>

        /// 摘要:

        ///     获取/设置角色实体实例的长整型编号值。

        /// </remarks>

        /// </summary>

        [Display(Name = "编号")]

        public long Id { get; set; }

        /// <summary>

        /// 【角色名】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定的角色名。

        /// 说明:

        ///     不区分大小写且必须具有唯一性。

        /// </remarks>

        /// </summary>

        [Display(Name = "名称")]

        [Required(ErrorMessage = "必须输入角色名。")]

        [Remote(action: "UniqueName", controller: "Role", AdditionalFields = nameof(Id))]

        [MaxLength(255)]

        public string Name { get; set; }

        /// <summary>

        /// 【启用?】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个值false(禁用)/true(默认值:启用),该值指示角色实体的1个指定实例是否处于启用状态。

        /// </remarks>

        /// </summary>

        [Display(Name = "可用")]

        public bool Active { get; set; }

        /// <summary>

        /// 【系统角色?】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个值false(不是系统角色)/true(默认值:系统角色),该值指示角色实体的1个指定实例是否是系统角色

        /// 说明:

        ///     名称不能被修改;不能被物理/逻辑删除;必须处于启用状态。

        /// </remarks>

        /// </summary>

        [Display(Name = "系统角色")]

        public bool IsSystemRole { get; set; }

        /// <summary>

        /// 【备注】

        /// <remarks>

        /// 摘要:

        ///     获取/设置1个指定角色的备注信息。

        /// </remarks>

        /// </summary>

        [Display(Name = "备注")]

        [MaxLength(500)]

        public string Remark { get; set; }

    }

}

2 Web.Areas.Admin.Controllers.RoleController

using Core.Domain.Customers;

using Microsoft.AspNetCore.Mvc;

using Services.Customers;

using Web.Areas.Admin.Models.Customers;

namespace Web.Areas.Admin.Controllers

{

    [Area("Admin")]

    public class RoleController : Controller

    {

        #region 拷贝构造方法与变量

        private readonly ICustomerService _customerService;

        /// <summary>

        /// 【拷贝构建方法】

        /// <remarks>

        /// 摘要:

        ///     依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。

        /// </remarks>

        /// </summary>

        public RoleController(ICustomerService customerService)

        {

            _customerService = customerService;

        }

        #endregion

        #region Razor页面中输入的自动验证

        /// <param name="name">被验证的角色名。</param>

        /// <param name="id">被验证角色名所对应的长整型编号值,默认值:0,用户角色实例的新建。</param>

        /// <summary>

        /// 【唯一角色名】

        /// <remarks>

        /// 摘要:

        ///     通过相应的参数实例,远程验证表单中所输入的用户名是否已经被注册。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     JSON编码格式的验证结果状态信息。

        /// </returns>

        /// </summary>

        public async Task<IActionResult> UniqueName(string name, long id = 0)

        {

            Role _role = (await _customerService.GetAllRolesAsync(true)).Where(role => role.Name.ToLower().Equals(name.Trim().ToLower())).FirstOrDefault();

            if (_role != null && _role.Id != id)

                return Json($"该角色名已经被使用。");

            return Json(true);

        }

        #endregion

        #region CURD

        /// <summary>

        /// 【角色列表】

        /// <remarks>

        /// 摘要:

        ///     获取角色实体的所有实例,为角色列表Razor页面的渲染显示提供数据支撑。

        /// </remarks>

        /// <returns>

        ///    1个列表实例,该实例存储着角色实体的所有实例。

        /// </returns>

        /// </summary>

        public async Task<IActionResult> Index()

        {

            IList<Role> _roleList = await _customerService.GetAllRolesAsync(true);

            return View(_roleList);

        }

        /// <summary>

        /// 【添加角色】

        /// <remarks>

        /// 摘要:

        ///     获取角色实体的1个空实例,为添加角色Razor页面的渲染显示提供数据支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     角色实体的1个空实例。

        /// </returns>

        /// </summary>

        public IActionResult Create()

        {

            return View();

        }

        /// <param name="model">角色模型记录的1个指定实例。</param>

        /// <summary>

        /// 【添加角色】

        /// <remarks>

        /// 摘要:

        ///     通过添加角色Razor页面中的数据,把角色实体的1个指定实例持久化到角色表中。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     角色模型记录的1个指定实例。

        /// </returns>

        /// </summary>

        [HttpPost]

        public async Task<IActionResult> Create(RoleModel model)

        {

            var role = new Role() { Name = model.Name, Active = true, IsSystemRole = model.IsSystemRole, Remark = model.Remark };

            await _customerService.InsertRoleAsync(role);

            ViewBag.RefreshPage = true;

            return View(model);

        }

        /// <param name="id">1个指定的长整型编号值。</param>

        /// <summary>

        /// 【修改角色】

        /// <remarks>

        /// 摘要:

        ///     获取角色实体的1个指定实例,为修改角色Razor页面的渲染显示提供数据支撑。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     角色模型记录的1个指定实例。

        /// </returns>

        /// </summary>

        public async Task<IActionResult> Edit(long id)

        {

            var role = await _customerService.GetRoleByIdAsync(id);

            var model = new RoleModel() { Id = role.Id, Name = role.Name, Active = role.Active, IsSystemRole = role.IsSystemRole, Remark = role.Remark };

            return View(model);

        }

        /// <param name="model">角色模型记录的1个指定实例。</param>

        /// <summary>

        /// 【修改角色】

        /// <remarks>

        /// 摘要:

        ///     通过更新角色Razor页面中的数据,把角色实体的1个指定实例持久化更新到角色表中。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     角色模型记录的1个指定实例。

        /// </returns>

        /// </summary>

        [HttpPost]

        public async Task<IActionResult> Edit(RoleModel model)

        {

            var role = new Role() { Id = model.Id, Name = model.Name, Active = model.Active, IsSystemRole = model.IsSystemRole, Remark = model.Remark };

            await _customerService.UpdateRoleAsync(role);

            ViewBag.RefreshPage = true;

            return View(model);

        }

        #endregion

    }

}

3 WebAreasAdminViewsRoleIndex.cshtml

@model IEnumerable<Core.Domain.Customers.Role>

<div class="content-header">

    <div class="container-fluid">

        <div class="row mb-2">

            <div class="col-sm-6">

                <div class="fs-3">角色列表</div>

            </div>

            <div class="col-sm-6">

                <ol class="breadcrumb float-sm-end">

                    <li class="breadcrumb-item">

                        @*resetRoleList:用于控制对弹出框的关闭和角色列表的刷新渲染*@

                        <button type="button" id="resetRoleList" class="btn btn-secondary me-3 d-none">

                            <i class="fa-solid fa-rotate-right"></i>

                            重置

                        </button>

                        <button type="button" onclick="OpenWindow('@(Url.Action("Create", "Role",  new {btnId = "resetRoleList"}))', 500, 500, true); return false;" class="btn btn-success float-end">

                            <i class="fas fa-plus"></i>

                            添加

                        </button>

                    </li>

                </ol>

            </div>

        </div>

    </div>

</div>

<!-- Main content -->

<div class="content">

    <div class="container-fluid">

        <div class="row">

            <div class="col-12">

                <!-- Default box -->

                <div class="card">

                    <div class="card-body">

                        <table class="table table-bordered">

                            <thead>

                                <tr>

                                    <th style="width: 100px">

                                        编号

                                    </th>

                                    <th>

                                        名称

                                    </th>

                                    <th style="width: 50px">

                                        可用

                                    </th>

                                    <th style="width: 50px">

                                        系统

                                    </th>

                                    <th>

                                        备注

                                    </th>

                                    <th class="text-center" style="width: 100px">

                                        操作

                                    </th>

                                </tr>

                            </thead>

                            <tbody>

                                @foreach (var item in Model)

                                {

                                    <tr>

                                        <td>

                                            @Html.DisplayFor(modelItem => item.Id)

                                        </td>

                                        <td>

                                            @Html.DisplayFor(modelItem => item.Name)

                                        </td>

                                        <td class="text-center">

                                            @if (item.Active)

                                            {

                                                <i class="fas fa-check fa-solid fa-bold fa-xl true-icon text-success"></i>

                                            }

                                            else

                                            {

                                                <i class="fas fa-times fa-solid fa-bold fa-xl false-icon text-danger"></i>

                                            }

                                        </td>

                                        <td class="text-center">

                                            @if (item.IsSystemRole)

                                            {

                                                <i class="fas fa-check fa-solid fa-bold fa-xl true-icon text-success"></i>

                                            }

                                            else

                                            {

                                                <i class="fas fa-times fa-solid fa-bold fa-xl false-icon text-danger"></i>

                                            }

                                        </td>

                                        <td>

                                            @Html.DisplayFor(modelItem => item.Remark)

                                        </td>

                                        <td class="text-center">

                                            <button type="button" onclick="OpenWindow('@Url.Content("~/Admin/Role/Edit/")' + @item.Id + '?btnId=resetRoleList' ,500, 500, true); return false;" class="btn btn-info btn-sm">

                                                <i class="fas fa-pencil-alt"></i>

                                                编辑

                                            </button>

                                        </td>

                                    </tr>

                                }

                            </tbody>

                        </table>

                    </div>

                </div>

                <!-- /.card -->

            </div>

        </div>

        <!-- /.row -->

    </div><!-- /.container-fluid -->

</div>

<!-- /.content -->

@section Scripts {

    <script type="text/javascript">

        $(document).ready(function () {

            //console.log('ready!');

            console.log(1111);

            //alert("删除学生的编号");

        });

        $("#resetRoleList").on("click", function () {

           window.location.href = "/Admin/Role/Index";

        });

    </script>

}

4 WebAreasAdminViewsRoleCreate.cshtml

@model RoleModel

@{

    ViewData["Title"] = "添加角色";

    Layout = "~/Areas/Admin/Views/Shared/_LayoutPopup.cshtml";

}

@if (ViewBag.RefreshPage == true)

{

    <script type="text/javascript">

        //如果重置按钮定义在查询表单中时,通过下1行语句实现对弹出框的关闭。

        //window.opener.document.forms['@(Context.Request.Query["formId"])'].@(Context.Request.Query["btnId"]).click();

        //如果重置按钮单独定义时,通过下1行语句实现对弹出框的关闭。

        window.opener.document.getElementById("@Context.Request.Query["btnId"]").click();

        window.close();

    </script>

}

<div class="content-header">

</div>

<!-- Main content -->

<div class="content">

    <div class="container-fluid">

        <div class="row">

            <div class="col-md-12">

                <div class="card card-primary card-outline">

                    <form asp-action="Create"

                          asp-route-btnId="@Context.Request.Query["btnId"]">

                        <div class="card-body">

                            <div class="mb-3">

                                <label asp-for="Name" class="control-label"></label>

                                <input asp-for="Name" class="form-control" />

                                <span asp-validation-for="Name" class="text-danger"></span>

                            </div>

                            <div class="mb-3">

                                <label class="form-check-label">

                                    <input class="form-check-input check25px" asp-for="IsSystemRole" />

                                    <label class="check25pxLabel">

                                        @Html.DisplayNameFor(model => model.IsSystemRole)

                                    </label>

                                </label>

                            </div>

                            <div class="fmb-3">

                                <label asp-for="Remark" class="control-label"></label>

                                <textarea asp-for="Remark" class="form-control" rows="3"></textarea>

                                <span asp-validation-for="Remark" class="text-danger"></span>

                            </div>

                            <div class="card-footer text-center">

                                <input type="submit" value="保存" class="btn btn-primary" />

                            </div>

                        </div>

                    </form>

                </div>

            </div>

        </div>

    </div>

</div>

@section Scripts {

    @{

        await Html.RenderPartialAsync("_ValidationScriptsPartial");

    }

}

5 WebAreasAdminViewsRoleEdit.cshtml

@model RoleModel

@{

    ViewData["Title"] = "修改角色";

    Layout = "~/Areas/Admin/Views/Shared/_LayoutPopup.cshtml";

}

@if (ViewBag.RefreshPage == true)

{

    <script type="text/javascript">

        //如果重置按钮定义在查询表单中时,通过下1行语句实现对弹出框的关闭。

        //window.opener.document.forms['@(Context.Request.Query["formId"])'].@(Context.Request.Query["btnId"]).click();

        //如果重置按钮单独定义时,通过下1行语句实现对弹出框的关闭。

        window.opener.document.getElementById("@Context.Request.Query["btnId"]").click();

        window.close();

    </script>

}

<div class="content-header">

</div>

<!-- Main content -->

<div class="content">

    <div class="container-fluid">

        <div class="row">

            <div class="col-md-12">

                <div class="card card-primary card-outline">

                    <form asp-action="Edit"

                          asp-route-btnId="@Context.Request.Query["btnId"]">

                        <div class="card-body">

                            <div class="d-none">

                                <input asp-for="Id" class="form-control" />

                            </div>

                            <div class="mb-3">

                                <label asp-for="Name" class="control-label"></label>

                                <input asp-for="Name" class="form-control" />

                                <span asp-validation-for="Name" class="text-danger"></span>

                            </div>

                            <div class="mb-3">

                                <label class="form-check-label">

                                    <input class="form-check-input check25px" asp-for="Active" />

                                    <label class="check25pxLabel">

                                        @Html.DisplayNameFor(model => model.Active)

                                    </label>

                                </label>

                            </div>

                            <div class="mb-3">

                                <label class="form-check-label">

                                    <input class="form-check-input check25px" asp-for="IsSystemRole" />

                                    <label class="check25pxLabel">

                                        @Html.DisplayNameFor(model => model.IsSystemRole)

                                    </label>

                                </label>

                            </div>

                            <div class="fmb-3">

                                <label asp-for="Remark" class="control-label"></label>

                                <textarea asp-for="Remark" class="form-control" rows="3"></textarea>

                                <span asp-validation-for="Remark" class="text-danger"></span>

                            </div>

                            <div class="card-footer text-center">

                                <input type="submit" value="保存" class="btn btn-primary" />

                            </div>

                        </div>

                    </form>

                </div>

            </div>

        </div>

    </div>

</div>

@section Scripts {

    @{

        await Html.RenderPartialAsync("_ValidationScriptsPartial");

    }

}

对以上功能更为具体实现和注释见230521_014ShopRazor(Role CURD Popup定义实现)。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。