当前位置:首页 > 技术 > 正文内容

ASP.NET MVC 查询参数封装与自动化验证模式

访客 技术 2026年9月1日 1

在 ASP.NET MVC 大量查询场景中,控制器方法常面临参数膨胀、重复校验、SQL 注入防护和时间范围逻辑混乱等问题。传统方式将校验逻辑散落在 Action 内,导致可读性差、复用率低、维护成本高。本文提出一种基于智能属性封装 + 延迟验证计算的轻量级解决方案,将数据接收、清洗、范围约束与时间归一化全部下沉至专用请求模型中。

问题驱动:从"扁平参数"到"结构化契约"

当查询接口需支持 10+ 字段(如用户 ID、姓名、状态码、等级区间、起止时间、分页配置、视图模式等),直接声明 Action 参数会迅速失控:

public ActionResult Search(
    int? id, string name, int? status, int? levelMin, int? levelMax,
    DateTime? start, DateTime? end, int page = 1, int size = 20,
    string viewMode = "list", string keyword = null)
{ ... }

此类签名难以理解语义,且每个字段需独立校验——例如 levelMin 必须 ≤ levelMaxstart 不得晚于 endend 不得超当前时刻,字符串需去空格防注入……逐行编写既冗长又易漏。

核心思想:让属性 getter 承担验证职责

不再依赖 Action 内部手动校验,而是设计一个请求模型类(如 SearchRequest),其所有公开属性均通过 get 访问器实现按需计算 + 自动修复

  • 值为 null 时赋予合理默认值(如分页页码默认为 1);
  • 超出业务范围时自动截断或重置(如等级强制限定在 [0,5]);
  • 时间字段自动归一化(如"开始时间"设为当日 00:00:00,"结束时间"设为当日 23:59:59);
  • 字符串自动 Trim 并过滤敏感字符(如 SQL 关键字、脚本标签);
  • 提供辅助属性(如 StartFormattedEndFormatted)返回已处理的字符串值。

重构后的控制器代码(极简风格)

public ActionResult Search(SearchRequest req)
{
    // ✅ 所有参数已自动清洗、验证、归一化
    var sql = $@"SELECT * FROM users 
                 WHERE id = {req.Id} 
                   AND name LIKE '%{req.Name}%'
                   AND created BETWEEN '{req.StartFormatted}' AND '{req.EndFormatted}'
                 LIMIT {req.PageSize} OFFSET {(req.PageIndex - 1) * req.PageSize}";

    var users = _db.Query<User>(sql);
    return View(users);
}

无需任何前置校验代码,模型自身保证输出值始终处于安全、可用、符合业务规则的状态。

关键实现:SearchRequest 模型示例

以下为精简重构版核心逻辑(移除冗余注释与枚举定义,优化正则与时间处理):

public class SearchRequest
{
    // 分页控制
    public int PageIndex { get; set; } = 1;
    public int PageSize { get; set; } = 20;

    // 用户基础信息
    private string _name;
    public string Name
    {
        get => SanitizeString(_name, allowEmpty: true);
        set => _name = value;
    }

    private int? _id;
    public int? Id
    {
        get => _id ?? null;
        set => _id = value >= 0 ? value : null;
    }

    // 等级范围([0,5])
    private int? _level;
    public int? Level
    {
        get => Clamp(_level, min: 0, max: 5);
        set => _level = value;
    }

    // 时间范围(自动归一化)
    private DateTime? _startTime;
    public DateTime? StartTime
    {
        get
        {
            if (!_startTime.HasValue) return null;
            var dt = _startTime.Value.Date;
            return dt > DateTime.Now.Date ? DateTime.Now.Date : dt;
        }
        set => _startTime = value;
    }

    private DateTime? _endTime;
    public DateTime? EndTime
    {
        get
        {
            if (!_endTime.HasValue) return null;
            var dt = _endTime.Value;
            if (dt > DateTime.Now) dt = DateTime.Now;
            return new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
        }
        set => _endTime = value;
    }

    // 格式化输出(供 SQL 或日志使用)
    public string StartFormatted => StartTime?.ToString("yyyy-MM-dd 00:00:00") ?? "";
    public string EndFormatted => EndTime?.ToString("yyyy-MM-dd 23:59:59") ?? "";

    // 工具方法
    private string SanitizeString(string input, bool allowEmpty = false)
    {
        if (string.IsNullOrWhiteSpace(input))
            return allowEmpty ? "" : null;

        var cleaned = input.Trim();
        if (ContainsDangerousPattern(cleaned))
            cleaned = "[CLEANED]";
        return cleaned;
    }

    private int? Clamp(int? value, int min, int max)
    {
        if (!value.HasValue) return null;
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }

    private static readonly Regex SqlPattern = new Regex(
        @"(?i)\b(select|insert|delete|update|create|drop|exec|declare|union|script|iframe|'|--|\bexec\b)", 
        RegexOptions.Compiled);

    private static bool ContainsDangerousPattern(string text) =>
        SqlPattern.IsMatch(text);
}

优势总结

  • 零校验侵入:Controller 层彻底摆脱 if 校验链,专注业务逻辑;
  • 强一致性保障:同一字段在任意 Action 中行为完全一致;
  • 可扩展性强:新增字段只需添加属性 + 对应 getter 逻辑,不破坏现有结构;
  • 调试友好:直接查看模型实例属性值,即为最终生效值;
  • 安全基线内置:SQL 注入防护、XSS 防御、越界保护均集中管控。

该模式适用于中大型 MVC 项目中高频查询场景,尤其适合需快速交付、长期维护的后台管理系统。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。