ASP.NET Core Razor Pages 教程六 处理表单

处理表单

任何成功的电子商务网站都需要能够处理订单。如果你没有客户的联系方式和送货地址就很难做到这一点。网站收集这类信息的方式是使用表单。

在本节中,你将向 Order 页面添加一个表单,同时还将向表单中添加验证,以确保信息的收集是有效和完整的。

准备工作

首先,添加以下的样式声明到 wwwroot/css 中的 site.css 文件:

label[for="OrderQuantity"]{
  padding-left: 15px;
}

#OrderQuantity{
  margin: 0 15px;
  max-width: 100px;
}

.order-calc{
  display: inline-block;
  margin: 0 10px;
}

.input-validation-error, .input-validation-error:focus {
  background: #ffccba;
  color: #d63301;
}

.form-control.input-validation-error:focus{
  border-color: #d63301;
  box-shadow: 0 0 0 0.2rem rgba(255, 123, 123, 0.5);
}
      
.field-validation-error {
  color: #be3e16;
}

.validation-summary-errors {
  color: #be3e16;
}

前三种风格纯粹是装饰性的。最后四种样式将有助于轻松地识别字段验证错误的位置。现在,将以下突出显示的行添加到 Order.cshtml.cs 文件中:

using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Bakery.Data;
using Bakery.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Bakery.Pages
{
    public class OrderModel : PageModel
    {
        private BakeryContext db;

        public OrderModel(BakeryContext db) => this.db = db;

        [BindProperty(SupportsGet =true)]
        public int Id { get; set; }
        public Product Product { get; set;}
        [BindProperty, EmailAddress, Required, Display(Name="Your Email Address")]
        public string OrderEmail { get; set; }
        [BindProperty, Required(ErrorMessage="Please supply a shipping address"), Display(Name="Shipping Address")]
        public string OrderShipping { get; set; } 
        [BindProperty, Display(Name="Quantity")]
        public int OrderQuantity { get; set; } = 1;

        public async Task OnGetAsync() => Product = await db.Products.FindAsync(Id);
    }
}

额外的 using 指令使用 DataAnnotations 命名空间。这个命名空间包含许多的属性,其中一些属性用于装饰已添加到 PageModel 的属性。OrderEmail 属性应用了 DataAnnotation 属性(以及 BindProperty属性):

  • EmailAddress - 电子邮件地址-它验证字符串是否与电子邮件地址的模式匹配
  • Required - 指定的值是必需的
  • Display - 允许您自定义属性在 UI 中使用的值

其中两个属性在验证中发挥作用,稍后你就可以看到了。OrderQuantity 属性分配的默认值为 1

添加表单

在 Order.cshtml 的底部添加以下几行:

<form method="post">
    <div class="row">
        <div class="col-3">
            <img src="~/Images/Products/Thumbnails/@Model.Product.ImageName" class="img-fluid img-thumbnail" alt="Image of @Model.Product.Name"/>
        </div>
        <div class="col-9">
            <ul class="orderPageList" data-role="listview">
                <li>
                    <div>
                        <p class="description">@Model.Product.Description</p>
                    </div>                
                </li>
                <li class="email">
                    <div class="form-group">
                        <label asp-for="OrderEmail"></label>
                        <input asp-for="OrderEmail" class="form-control form-control-sm" />                
                        <span asp-validation-for="OrderEmail"></span>
                    </div>
                </li>
                <li class="shipping">
                    <div class="form-group">
                        <label asp-for="OrderShipping"></label>
                        <textarea rows="4" asp-for="OrderShipping" class="form-control form-control-sm"></textarea>               
                        <span asp-validation-for="OrderShipping"></span>
                    </div>
                </li>
                <li class="quantity">
                    <div class="form-group row">
                        <label asp-for="OrderQuantity" class="col-1 col-form-label"></label>
                        <input asp-for="OrderQuantity" class="form-control form-control-sm"/>
                        x
                        <span class="order-calc" id="orderPrice">@Model.Product.Price.ToString("f")</span>
                        =
                        <span class="order-calc" id="orderTotal">@Model.Product.Price.ToString("f")</span>
                        <span asp-validation-for="OrderQuantity"></span>
                    </div>
                </li>
            </ul>
            <p class="actions">
                <input type="hidden" asp-for="Product.Id" />
                <button class="btn btn-danger order-button">Place Order</button>
            </p>
        </div>
    </div>
</form>


@section scripts{
<script type="text/javascript">
    $(function () {
        var price = parseFloat($("#orderPrice").text()).toFixed(2),
            total = $("#orderTotal"),
            orderQty = $("#OrderQuantity");

        orderQty.on('change', function () {
            var quantity = parseInt(orderQty.val());
            if (!quantity || quantity < 1) {
                orderQty.val(1);
                quantity = 1;
            } else if (quantity.toString() !== orderQty.val()) {
                orderQty.val(quantity);
            }
            total.text("$" + (price * quantity).toFixed(2));
        });
    });
</script>
}

表单使用标记帮助器来呈现标签,输入和验证消息。 目标输入元素的标记助手特别强大。 PageModel 属性传递给输入标记助手上的 asp-for 属性。 然后,输入标记帮助程序根据属性呈现正确的 name 属性,以便模型绑定可以无缝地工作。 分配给属性的任何值都将分配给输入。 输入的 type 属性是根据属性的数据类型生成的。

表单本身也将由标记帮助器作为目标,它将确保呈现请求验证令牌。

代码以JavaScript块结尾,根据订购的商品数量计算总价。 <script> 标记放在名为 scripts@section 块中,该块被小心地放置在布局页面中,以便在其他站点范围的脚本(包括脚本块所依赖的 jQuery)下面呈现其内容。

接下来,您需要一个处理程序方法来处理表单。将以下内容添加到 Order.cshtml.cs 文件中:

public async Task<IActionResult> OnPostAsync()
{
    Product = await db.Products.FindAsync(Id);
    if(ModelState.IsValid){
        return RedirectToPage("OrderSuccess");
    }
    return Page();
}

目前,这个方法所做的只是检查 ModelState.IsValid。 这确保模型绑定特性得到满足, 所有提交的值都通过验证检查, 并且所有需要的值都存在。 如果验证中有任何错误, 则向 ModelState 对象添加条目,并重新显示当前页面(return page())。 如果提交是有效的, 用户将被重定向到 OrderSuccess 页面, 接下来您将添加该页面。

此模式称为 Post-Redirect-Get(PRG),旨在最大程度地减少双重发布导致重复提交的可能性。

如果表单中存在错误,则会再次显示该页面,验证标记帮助程序将显示验证错误的详细信息。

现在使用以下命令将 OrderSuccess 页面添加到应用程序:

dotnet new page --name OrderSuccess --output Pages --namespace Bakery.Pages

使用以下的代码来替换 OrderSuccess.cshtml 文件的内容:

@page
@model Bakery.Pages.OrderSuccessModel
@{
}
<ol id="orderProcess">
    <li><span class="step-number">1</span>Choose Item</li>
    <li><span class="step-number">2</span>Details &amp; Submit</li>
    <li class="current"><span class="step-number">3</span>Receipt</li>
</ol>
<h1>Order Confirmation</h1>

现在是测试表单是否正常工作和处理的时候了。输入 dotnet run 启动应用程序,然后导航到 https://localhost:5001。选择 Lemon Tart 产品,并确保表单显示正确:
Lemon Tart

现在,在不输入电子邮件或送货地址的情况下按下 Place Order 按钮, 这样您就可以测试验证了。 两个字段都应该变成粉红色,错误消息应该出现在它们下面:
error

您可以执行进一步的测试,比如删除 Quantity 框中的值, 或者在电子邮件输入中输入一个随机字符串。 每次提交表单后, 错误消息都会出现。

添加客户端验证

此时,所有验证都在服务器上执行。表单被发布,整个页面被重新呈现以向用户提供反馈。在开发站点时,您并不会真正注意到这种往返,因为客户机和服务器在同一台机器上。然而,在实际应用程序中,在用户收到任何反馈之前可能会有一些延迟。在客户端验证将为用户提供即时反馈。

在客户机上验证应该只被看作是对用户的一种礼貌。它永远不应该取代服务器端验证。对于具有少量 HTML/JavaScript 知识的人来说,绕过客户端验证是非常容易的。

Razor Pages 默认包含客户端验证(Client side validation),但需要启用它。 您可以通过在页面中包含 jQuery Validation 和 jQuery Unobtrusive Validation 库来实现。 包含这些脚本的代码已在名为 _ValidationScriptsPartial.cshtml 的部分中提供,该部分位于 Pages/Shared 文件夹中。 要包含它,只需将部分标记助手添加到 Order.cshtml 中的脚本部分,如下面突出显示的代码行所示:

@section scripts{
<partial name="_ValidationScriptsPartial"></partial>
<script type="text/javascript">
    $(function () {
        var price = parseFloat($("#orderPrice").text()).toFixed(2),
            total = $("#orderTotal"),
            orderQty = $("#OrderQuantity");

        orderQty.on('change', function () {
            var quantity = parseInt(orderQty.val());
            if (!quantity || quantity < 1) {
                orderQty.val(1);
                quantity = 1;
            } else if (quantity.toString() !== orderQty.val()) {
                orderQty.val(quantity);
            }
            total.text("$" + (price * quantity).toFixed(2));
        });
    });
</script>
}

现在,如果您尝试提交缺少值的表单,则会显示错误,而不会将表单实际发布到服务器。 如果您提供满足验证的值,您应该进入 OrderSuccess 页面:
orderSuccess

接要

在本节中,您已经使用标记助手创建了一个表单,并添加了服务器端和客户端验证。您已经成功地测试了表单。到目前为止,您还没有对发布的值做任何有意义的事情。在下一节中,您将使用已发布的值来构造电子邮件并发送它。

posted on 2019-02-23 20:16  路盟  阅读(1085)  评论(0编辑  收藏  举报

导航