首页 > 编程语言 >ASP.NET Core应用程序11:使用模型绑定

ASP.NET Core应用程序11:使用模型绑定

时间:2024-06-15 15:54:45浏览次数:29  
标签:11 Core ASP Name 模型 绑定 Data public 属性

  模型绑定是使用从 HTTP 请求获得的数据值,创建操作方法和页面处理程序所需的对象的过程。本章描述模型绑定系统的工作方式;显示它如何绑定简单类型、复杂类型和集合;并演示如何控制流程,以指定请求的哪一部分提供应用程序所需的数据值。
  本章介绍了模型绑定特性,展示了如何使用带有参数和属性的模型绑定,如何绑定简单和复杂类型,以及绑定到数组和集合所需的约定。还解释了如何控制请求的哪一部分用于模型绑定,以及如何控制何时执行模型绑定。

1 准备工作

  本章继续使用上一章项目。
  修改 Views/Form 文件夹中 Form.cshtml。

@model Product
@{
    Layout = "_SimpleLayout";
}

<h5 class="bg-primary text-white text-center p-2">HTML Form</h5>

<form asp-action="submitform" method="post" id="htmlform">
    <div class="form-group">
        <label asp-for="Name"></label>
        <input class="form-control" asp-for="Name" />
    </div>
    <div class="form-group">
        <label asp-for="Price"></label>
        <input class="form-control" asp-for="Price" />
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

  在 Models 文件夹的 Product.cs,注释掉已应用于 Product 模型类的 DisplayFormat 属性。

//[DisplayFormat(DataFormatString = "{0:c2}", ApplyFormatInEditMode = true)]

  浏览器请求http://localhost:5000/controllers/form

2 理解模型绑定

  模型绑定是 HTTP 请求和操作或页面处理程序方法之间的桥梁。大多数 ASP.NET Core 应用程序在某种程度上依赖于模型绑定。
  通过使用浏览器请求 http://localhost:5000/controllers/form/index/5。这个 URL, 包含想要査看的 Product 对象的 Productld 属性值。URL 的这一部分对应于控制器路由模式定义的 id 参数名称匹配。

public async Task<IActionResult> Index(long id = 1)

  在 MVC 框架调用操作方法之前需要为 id 参数设置一个值,而找到一个合适的值是模型绑定系统得职责。模型绑定系统依赖于模型绑定器,模型绑定器是负责从请求或应用程序的某个部分提供数据值的组件。

  默认的模型绑定在以下四个地方寻找数据值:

  • 表单数据
  • 请求主体(仅适用于用 ApiController 装饰的控制器).
  • 路由段变量。
  • 查询字符串

  按顺序检查每个数据源,直至找到参数的值为止。示例请求中没有表单数据,因此在那里不会找到任何值,并且表单控制器没有使用 ApiController 属性装饰,因此不会检査请求主体,下一步是检查路由数据,它包含一个名为 id 的段变量。这允许模型绑定系统提供一个值来允调用索引操作方法。在找到合适的数据值后停止搜索,所以就不会搜索查询字符串数据值。

  知道寻找数据值的顺序是很重要的,因为一个请求可以包含多个值,比如这个URL:http://localhost:5000/controllers/form/index/3?id=1
  路由系统将处理请求,并将 URL 模板中的 id 段与值 3 匹配,查询字符串包含 id 值 1,由于搜索查询字符串之前的路由数据,因此索引操作方法将接收值 3,而忽略査询字符串的值。另一方面,如果请求没有 id 段的 URL:http://localhost:5000/controllers/form/index?id=1,则将检査查询字符串。

3 绑定简单数据类型

  请求数据值必须转换为C#值,这样它们才能用于调用操作或页面处理程序方法。简单类型是源自请求中的一项数据的值,该数据项可以从字符串中解析。这包括数值、bool值、日期和字行串值。
  用于简单类型的数据绑定很容易从请求中提取单个数据项,而不必通过上下文数据查找定义的位置。日系如下 FormController.cs 向 Form 控制器方法定义的 SubmitForm 操作方法添加了参数,以便模型绑定器用于提供 name 和 price 值。

[HttpPost]
public IActionResult SubmitForm(string name, decimal price)
{
    TempData["name param"] = name;
    TempData["price param"] = price.ToString();
    return RedirectToAction(nameof(Results));
}

绑定 Razor Pages 中的简单数据类型
  Razor Pages 可以使用模型绑定,但是必须注意确保表单元素的 name 属性的值与处理程序方法参数的名称相匹配,如果 asp-for 属性用来选择嵌套属性,则可能不会出现这种情况。为了确保名称匹配,可以显式定义 name 属性。修改 Pages 文件下 FormHandler.cshtml。

<div class="form-group">
    <label>Name</label>
    <input class="form-control" asp-for="Product.Name" name="name" />
</div>
<div class="form-group">
    <label>Price</label>
    <input class="form-control" asp-for="Product.Price" name="price" />
</div>
public IActionResult OnPost(string name, decimal price)
{
    TempData["name param"] = name;
    TempData["price param"] = price.ToString();
    return RedirectToPage("FormResults");
}

4 绑定复杂类型

  模型绑定系统在处理复杂类型时非常出色,复杂类型是不能从单个字符串值解析的任何类型。可使用绑定器创建完整的 Product 对象,而不是处理像 name 和 price 一样的单独的值。修改 FormController.cs。

[HttpPost]
public IActionResult SubmitForm(Product product)
{
    TempData["product"] = System.Text.Json.JsonSerializer.Serialize(product);
    return RedirectToAction(nameof(Results));
}

  请求提交后,返回一个json字符串 {"ProductId":0,"Name":"Kayak","Price":100.00,"CategoryId":0,"Category":null,"SupplierId":0,"Supplier":null},示例提供了 Name 和 Price 属性的值,但是 Produclid、Caiegoryld 和 SupplierId 属性为 0,而 Category 和 Supplier 属性为空。

4.1 绑定到属性

  使用参数进行模型绑定不适合 Razor 页面开发风格,因为参数经常重复页面模型类定义的属性。更好的方式是使用现有属性进行模型绑定。修改 Pages 文件下 FormHandler.cshtml 如下。

...
<input class="form-control" asp-for="Product.Name"/>
...
<input class="form-control" asp-for="Product.Price"/>
...
[BindProperty]
public Product Product { get; set; }
public IActionResult OnPost()
{
    TempData["product"] = System.Text.Json.JsonSerializer.Serialize(Product);
    return RedirectToPage("FormResults");
}

  用 BindProperty 修饰属性表明它的属性应该服从模型绑定过程,这意味着 OnPost 处理程序方法可以在不声明参数的情况下获得它需要的数据。当使用 BindProperty 特性时,模型绑定器在定位数据值时使用属性名,因此不需要添加到输入元素的显式 name 特性。
  默认情况下,BindProperty 不会绑定 GET 请求的数据,但这可以通过将 BindProperty 特性的 SupportsGet 参数设置为 tmue 来更改。

4.2 绑定嵌套的复杂类型

  如果使用复杂类型来定义受模型绑定约束的属性,则使用属性名作为前缀重复模型绑定过程。例如,Product 类定义 Category 属性,其 Category 是复杂类型,修改 Vews/Fom 文件夹的 Form.cshtml 如下。

<div class="form-group">
    <label>Category Name</label>
    <input class="form-control" name="Category.Name" value="@Model.Category.Name" />
</div>

  name 特性组合了由句点分隔的属性名称。在本例中,元素用于给视图模型的 Categoy 属指定的对象的 Name 属性,因此 Name 属性设置为 Category.Name。当应用 asp-for 特性时,输入元素标签助手将自动为 name 特性使用这种格式,如下所示。

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

4.3 选择性的绑定属性

  一些模型类定义了一些敏感的属性,用户不应该为这些属性指定值。为了防止模型绑定器使用敏感属性的值,可以指定应该绑定的属性列表。
  在 Controllers 文件夹的 FormController.cs 文件中有选择地绑定属性。

[HttpPost]
public IActionResult SubmitForm([Bind("Name", "Category")] Product product)
{
    TempData["name"] = product.Name;
    TempData["price"] = product.Price.ToString();
    TempData["category name"] = product.Category.Name;
    return RedirectToAction(nameof(Results));
}

  为操作方法参数返回了 Product 类型,该参数用 Bind 特性修饰,以指定应该包含在模型绑定过程中的属性名称。这个示例告诉模型绑定特性寻找Name 和 Category 属性的值,这将从流程中排除其他任何属性。
  另外 BindNever 特性可以从模型绑定器中排除了一个属性,与上面的效果相同。

using Microsoft.AspNetCore.Mvc.ModelBinding;
[BindNever]
public decimal Price { get; set; }

5 绑定到数组和集合

5.1 绑定到数组

  给 Pages 文件夹添加一个名为 Bindings.cshtml 的 Razor Pages。

@page "/pages/bindings"
@model BindingsModel
@using Microsoft.AspNetCore.Mvc
@using Microsoft.AspNetCore.Mvc.RazorPages

<div class="container-fluid">
    <div class="row">
        <div class="col">
            <form asp-page="Bindings" method="post">
                <div class="form-group">
                    <label>Value #1</label>
                    <input class="form-control" name="Data" value="Item 1" />
                </div>
                <div class="form-group">
                    <label>Value #2</label>
                    <input class="form-control" name="Data" value="Item 2" />
                </div>
                <div class="form-group">
                    <label>Value #3</label>
                    <input class="form-control" name="Data" value="Item 3" />
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
                <a class="btn btn-secondary" asp-page="Bindings">Reset</a>
            </form>
        </div>
        <div class="col">
            <ul class="list-group">
                @foreach (string s in Model.Data.Where(s => s != null))
                {
                    <li class="list-group-item">@s</li>
                }
            </ul>
        </div>
    </div>
</div>

@functions
{
    public class BindingsModel : PageModel
    {
        [BindProperty(Name = "Data")]
        public string[] Data { get; set; } = Array.Empty<string>();
    }
}

  数组的模型绑定需要将 name 特性设置为将提供数组值的所有元素的相同值。这个页面显示三个输入元素,他们的 name 特性值都是 Data。为让模型绑定器找到数组值,用 BindProperty 特性装饰了页面模型的 Data 属性,并使用了 Name 参数。
  提交 HTML 表单时,将创建一个新数组,并使用来自所有三个输入元素的值填充该数组,这些值将显示给用户。要查看绑定过程,请求 http://localhost:5000/pages/bindings

  默认情况下,数组是按照从浏览器接收表单值的顺序填充的,这个顺序通常是定义 HTML 元素的顺序。如果需要覆盖默认值,则可以使用 name 特性指定数组中值的位置。

<input class="form-control" name="Data[1]" value="Item 1" />
<input class="form-control" name="Data[0]" value="Item 2" />
<input class="form-control" name="Data[2]" value="Item 3" />

5.2 绑定到简单集合

  模型绑定流程可以创建集合和数组。对于序列集合,例如列表和集合,只更改模型绑定器用的属性或参数的类型。

[BindProperty(Name = "Data")]
public SortedSet<string> Data { get; set; } = new SortedSet<string>();

  将 Data 属性的类型改为 SortedSet。模型绑定过程用来自输入元素的值填充集合,这些值按字母顺序排序。

5.3 绑定到字典

  模型绑定到字典时为键值对。为集合提供值的所有元素都必须共享一个公共前缀(在本例中为 Data),后面跟着方括号中的键值。

<input class="form-control" name="Data[first]" value="Item 1" />
<input class="form-control" name="Data[second]" value="Item 2" />
<input class="form-control" name="Data[third]" value="Item 3" />
<div class="col">
    <table class="table table-sm table-striped">
        <tbody>
            @foreach (string key in Model.Data.Keys)
            {
                <tr>
                    <th>@key</th>
                    <td>@Model.Data[key]</td>
                </tr>
            }
        </tbody>
    </table>
</div>
[BindProperty(Name = "Data")]
public Dictionary<string, string> Data { get; set; }
    = new Dictionary<string, string>();

5.4 绑定到复杂类型的集合

@page "/pages/bindings"
@model BindingsModel
@using Microsoft.AspNetCore.Mvc
@using Microsoft.AspNetCore.Mvc.RazorPages

<div class="container-fluid">
    <div class="row">
        <div class="col">
            <form asp-page="Bindings" method="post">
                @for (int i = 0; i < 2; i++)
                {
                    <div class="form-group">
                        <label>Name #@i</label>
                        <input class="form-control" name="Data[@i].Name" 
                            value="Product-@i" />
                    </div>
                    <div class="form-group">
                        <label>Price #@i</label>
                        <input class="form-control" name="Data[@i].Price" 
                            value="@(100 + i)" />
                    </div>
                }
                <button type="submit" class="btn btn-primary">Submit</button>
                <a class="btn btn-secondary" asp-page="Bindings">Reset</a>
            </form>
        </div>
        <div class="col">
            <table class="table table-sm table-striped">
                <tbody>
                    <tr><th>Name</th><th>Price</th></tr>
                    @foreach (Product p in Model.Data)
                    {
                        <tr>
                            <th>@p.Name</th>
                            <td>@p.Price</td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

@functions
{
    public class BindingsModel : PageModel
    {
        [BindProperty(Name = "Data")]
        public Product[] Data { get; set; } = Array.Empty<Product>();

    }
}

6 指定模型绑定源

  默认的模型绑定过程在四个地方查找数据,但也可以覆盖默认搜索,指定绑定源。

  模型绑定源特性:

名称 描述
FromForm 该属性用子选择表单数据作为绑定数册的源
FromRoute 该属性用于选择作为绑定数据源的路由系统
FromQuery 该属性用于选择查询字符串作为绑定数据的源
FromHeader 该属性用于选择一个请求头作为绑定数据的源
FromBody 该属性用于指定应该将请求体用作绑定数据的源

  模型绑定源特性:
此URL:http://localhost:5000/controllers/Form/Index/5?id=1按照默认会查出 id 为 5 的数据,id 为 1 将被忽略。但如果将 FromQuery 特性应用于索引操作方法定义的 id 参数覆盖默认,那将查询 id 为 1的数据。

public async Task<IActionResult> Index([FromQuery] long? id)

7 手动模式绑定

  模型绑定源特性:
当为操作或处理程序方法定义参数或应用 BindProperty 属性时,将自动应用模型绑定。如果始终如一地遵循名称约定,并且总是希望应用该过程,那么自动模型绑定可以很好地工作。如果需要控制绑定过程,或者希望有选择地执行绑定,那么可以手动执行模型绑定。

@page "/pages/bindings"
@model BindingsModel
@using Microsoft.AspNetCore.Mvc
@using Microsoft.AspNetCore.Mvc.RazorPages

<div class="container-fluid">
    <div class="row">
        <div class="col">
            <form asp-page="Bindings" method="post">
                <div class="form-group">
                    <label>Name</label>
                    <input class="form-control" asp-for="Data.Name" />
                </div>
                <div class="form-group">
                    <label>Price</label>
                    <input class="form-control" asp-for="Data.Price"
                           value="@(Model.Data.Price + 1)" />
                </div>
                <div class="form-check m-2">
                    <input class="form-check-input" type="checkbox" name="bind"
                           value="true" checked />
                    <label class="form-check-label">Model Bind?</label>
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
                <a class="btn btn-secondary" asp-page="Bindings">Reset</a>
            </form>
        </div>
        <div class="col">
            <table class="table table-sm table-striped">
                <tbody>
                    <tr><th>Name</th><th>Price</th></tr>
                    <tr>
                        <td>@Model.Data.Name</td>
                        <td>@Model.Data.Price</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

@functions {

    public class BindingsModel : PageModel
    {

        public Product Data { get; set; }
            = new Product() { Name = "Skis", Price = 500 };

        public async Task OnPostAsync([FromForm] bool bind)
        {
            if (bind)
            {
                await TryUpdateModelAsync<Product>(Data,
                    "data", p => p.Name, p => p.Price);
            }
        }
    }
}

  手动模型绑定使用 TryUpdateModelAsync 方法执行,该方法由 PageModel 和 ControllerBase类提供,这意味着它对 Razor Pages 和 MVC 控制器都可用。
  这个例子混合了自动和手动的模型绑定。OnPostAsync 方法使用自动模型绑定来接收其绑定参数的值,该参数已经用 FromFomm 特性进行了修饰。如果参数的值为 true,则使用 TryUpdateModelAsync 方法应用模型绑定。TryUpdateModelAsync 方法的参数是将被模型绑定的对象、值的前缀和一系列选择将包含在流程中的属性的表达式。

标签:11,Core,ASP,Name,模型,绑定,Data,public,属性
From: https://www.cnblogs.com/nullcodeworld/p/18249382

相关文章

  • CC2500和CC1101移植说明
    主要通过如何移植、移植注意、关于芯片配置、如何生成导出配置四大步骤来说明CC2500和CC1101移植首先通过下图1这个宏进行选择 &如何移植要移植的部分在CC2500_hal.c和CC2500_hal.h中, 搜索"//移植"就可以定位到库所需的依赖,需要根据您的环境实现这些函数&移植......
  • 4-字符串-11-反转字符串-LeetCode344
    4-字符串-11-反转字符串-LeetCode344LeetCode:题目序号344更多内容欢迎关注我(持续更新中,欢迎Star✨)Github:CodeZeng1998/Java-Developer-Work-Note技术公众号:CodeZeng1998(纯纯技术文)生活公众号:好锅(Lifeismorethancode)CSDN:CodeZeng1998其他平台:CodeZeng1998、......
  • 【SPARK-CORE】checkpoint机制
    本文主要介绍SPARKRDD的checkpoinnt机制 checkpoint机制介绍checkpoint是讲RDD保存到可靠的存储中的机制,主要目的是提高应用的容错能力和持久性。Checkpointing将数据从内存中转移到磁盘存储,使得在出现节点故障时,Spark可以从存储中恢复数据,而不需要重新计算所有的数据。这......
  • DreamJudge-1177-查找学生信息
    1.题目描述TimeLimit:1000msMemoryLimit:32768mb“臭味相投”——这是我们描述朋友时喜欢用的词汇。两个人是朋友通常意味着他们存在着许多共同的兴趣。然而作为一个宅男,你发现自己与他人相互了解的机会并不太多。幸运的是,你意外得到了一份北大图书馆的图书借阅记录,于......
  • ASP.NET Core之使用OpenTelemetry的日志管理
    一、前言当前软件架构演变由单体架构=>分布式架构(SOA)=>微服务架构(mircoservice)=>云原生架构(cloudnative),所以架构的演变导致对系统日志、监控、链路等统称为观测性方案提出巨大的挑战。在单体架构时代,借助丰富的日志库基本满足对日志管理,但是面对分布式、微服务的架构盛行,......
  • 116. 小欧的卡牌(卡码网周赛第十七期(23年oppo提前批B组笔试真题))
    116.小欧的卡牌(卡码网周赛第十七期(23年oppo提前批B组笔试真题))题目描述小欧有n张卡牌,第i张卡牌的正面写了个数字ai,背面写了个数字bi。小欧对于每张卡牌可以选择一面向上,她希望最终向上的数字之和为3的倍数。你能告诉小欧有多少方案吗?由于答案过大,请对10^9+7......
  • 115. 组装手机(卡码网周赛第十七期(23年oppo提前批B组笔试真题))
    115.组装手机(卡码网周赛第十七期(23年oppo提前批B组笔试真题))题目描述小欧是手机外壳供应商,小蕊是手机零件供应商。小欧已经生产了n个手机外壳,第i个手机外壳售价ai元,小蕊生产了n个手机零件,第i个手机零件售价bi元。在组装手机中,一个手机外壳与一个手机零件可......
  • QT210开发板学习(1):SEC S5PC110 Test B/D驱动安装
    把开发板的开关拨到USBBOOT,通过USB线连接到开发板的OTG口,打开板上总电源,会提示驱动安装失败我们需要下载驱动(win7-64-DNW-USB)https://github.com/joyjohn131/QT210/tree/main/1打开dseo13b.exe,依次点击Next,Yes选择"EnableTestMode","Next",提示完成开启选择"SignaS......
  • 每日一题——Python实现PAT乙级1104 天长地久(举一反三+思想解读+逐步优化)七千字好文
    一个认为一切根源都是“自己不够强”的INTJ个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数Python-3.12.0文档解读目录初次尝试点评时间复杂度分析空间复杂度分析综合分析我要更强时间复杂度分析空间复杂度分析综合分析哲学和......
  • 代码随想录算法训练营第11、12天 | 逆波兰表达式、滑动窗口最大值、前 K 个高频元素
    逆波兰表达式题目https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/逆波兰表达式代码随想录https://programmercarl.com/0150.逆波兰表达式求值.html#其他语言版本滑动窗口最大值https://leetcode.cn/problems/sliding-window-maximum/滑动窗口......