9:输入Model和防止重复Post

 ♣ 视频地址:https://www.bilibili.com/video/av38392956/?p=8

这节讲输入Model,通过Action传入一个Model,传入进来的Model通常是用来建立数据,或者是修改数据

想要从客户端传进来Model,那么用户就需要输入Model及Model信息,在Html里输入Model信息通常是使用Form,所有我们想输入的信息肯定得有一个页面并且页面里有Form,所以首先我们需要个方法导航到这个Form页面

我们可以通过HttpGet请求进入某个Action,然后Action返回含有Form的View就可以了

那么Form里面的栏位是如何对应Model的各个字段呢,就是通过name属性来对应上的

• <input type="text" name="FirstName">

那么对应上之后是怎么传递呢?用http post来做操作

• HTTP POST

 

下面我们做跳转含有Form的页面,首先在Index.cshtml页加上一个跳转

   <div>
        <a asp-action="Create">添加一个学生</a>
    </div>

然后在HomeController里添加Action

       public IActionResult Create()
        {
            return View();
        }

 接着我们添加Create视图

@using Tutorial.Web.Model
@model StudentCreateViewModel

    <h1>创建学生</h1>
    <form action="/" method="post">
        <input asp-for="FirstName" /><br /><br />

        <input asp-for="LastName" /><br /><br />

        <input asp-for="BirthDate" type="date" /><br /><br />

        <select asp-for="Gender" asp-items="Html.GetEnumSelectList<Gender>()">
        </select>
        <br /><br />
        <button type="submit" name="save">保存</button>
    </form>

上面有个Gender类,是我从Model里新加的一个枚举

namespace Tutorial.Web.Model
{
    public enum Gender
    {
        男 = 1,
        女 = 0,
        其他 = 2
    }
}

我们运行,并且看下它生成的Html代码,可以看到有一个input,它的类型是hidden,它的value是一串token,我们看它的name是“RequestVerificationToken”,它是用来验证这个请求的,用来防止CSRF(跨站请求伪造)

通过这个token,我们可以保证提交进来的Form是从我的网站的这个页面提交的,而不是一些恶意网站


下面我们研究如何把Form里的栏位转化成一个Model,实际这个不是我们手动来做的,而是MVC框架自动去做的

 

我们Form表单的action的地址就是本身进来的地址/Home/Create,这个地址是HttpGet请求,但是提交之后走的是post请求,HttpPost,所以我们回到HomeController,继续建立一个名为Create,但是Method为HttpPost,这里我们新建了一个ViewModel,就是StudentCreateViewModel,为什么不用Student类呢?

因为我们要尽可能的保证Form里传递的参数和我们接收类型所有的参数要一直,Student有个Id属性,而Form表单里没有,不一致,所以我们新建一个StudentCreateViewModel来对应Form表单里的各项

  [HttpPost]
        public IActionResult Create(StudentCreateViewModel student)
        {
            var newstudent = new Student
            {
                FirstName = student.FirstName,
                LastName = student.LastName,
                BirthDate = student.BirthDate,
                Gender = student.Gender
            };

            var newModel = _repository.Add(newstudent);
            return View("Detail", newModel);
        }

然后我们在接口里添加,如下图

接着我们去服务类InMemoryRepository去实现这个接口

   public Student Add(Student newModel)
        {
            var maxId = _students.Max(x => x.Id);
            newModel.Id = maxId + 1;
            _students.Add(newModel);
            return newModel;
        }

现在呢,我们来完善下Detail这个视图

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Detail</title>
</head>
<body>
    <div>学生信息</div>
    <div>ID:@Model.Id</div>
    <div>
        姓名:@Model.FirstName @Model.LastName
    </div>
    <div>
        性别:@Model.Gender
    </div>
    <div>
        出生日期:@Model.BirthDate.ToString("yyyy-MM-dd")
    </div>

    <a asp-action="Index">返回Home</a>
</body>
</html>

这时候我们就运行,添加会发现在明细页可以展示新添的信息,如下图左,但是点击返回Home的时候,数据却没加载出来,如下图右

  

那为什么新添的数据没有?这是因为我们注册容器InMemoryRepository的时候选择它的声明周期是Scoped,就是每一次Http请求都会创建一个新的实例,所以在跳回Home页面的时候,又变成了新的请求,那解决办法就是我们把它改成Singleton即可

   public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSingleton<IWelcomeService, WelcomeService>();
            services.AddSingleton<IRepository<Student>, InMemoryRepository>();
        }

 这时候我们再运行看下结果,发现添加进来了,如下图

   

 这时候还会有个问题,就是我们再添加后的页面F5刷新,就会提示重新提交表单,记住这个地址/Home/Create

 

这时候我们一直点继续,就会一直提交,就会出现下面的场景,如图

那接下来我们就来解决,或者避免这个问题

POST-REDIRECT-GET

我们post之后,重定向REDIRECT新的页面,然后在新的页面获取GET数据,我们改下代码,如下图

 这时候我们添加一个人,可以看到地址变成了/Home/Detail/4,而不是上面之前那个/Home/Create了,这时候我们刷新页面也不会重新提交表单了

 然后返回Home,可以看到数据都正确,如下图

 

posted @ 2019-08-02 09:35  大北票  阅读(279)  评论(0编辑  收藏  举报