最近新建 Asp.net Core MVC 项目的时候不小心选错了个模板,发现了一种新的项目模板。它使用cshtml视图模板,但是没有Controller文件夹。后来才发现这是ASP.NET Core框架新推出的Razor Pages技术。

什么是Razor Pages

“Razor Pages 使编码更加简单更加富有生产力”这是微软说的==!。Razor Pages 简化了传统的mvc模式,仅仅使用视图跟模型来完成网页的渲染跟业务逻辑的处理。模型里包含了数据跟方法,通过绑定技术跟视图建立联系,这就有点像服务端的绑定技术。下面使用一个标准的CRUD示例来演示Razor Pages的开发,并且简单的探索一下它是如何工作的。

新建Razor Pages项目

在visual studio中新建Razor Pages项目。



项目结构



新建项目的目录结构比MVC项目简单。它没有Controllers目录,Pages有点像MVC项目的Views目录,里面存放了cshtml模板。随便点开一个cshtml文件,发现它都包含了一个cs文件。这是跟MVC项目最大的不同,这个结构让人回忆起那古老的WebForm技术,o(╥﹏╥)o 。

新建Razor Page

我们模拟开发一个学生管理系统。一共包含4个页面:列表页面、新增页面、修改页面、删除页面。首先我们新建一个列表页面。

在Pages目录下面新建Student目录。在Student目录下新建4个Razor page名叫:List、Add、Update、Delete。



建好后目录结构是这样:

模拟数据访问仓储

由于这是个演示项目,所以我们使用静态变量来简单模拟下数据持久。

在项目下新建一个Data目录,在目录下新建Student实体类:

    public class Student
{
public int Id { get; set; }
public string Name { get; set; } public string Class { get; set; } public int Age { get; set; } public string Sex { get; set; }
}

在Data目录下新建IStudentRepository跟StudentRepository类:

    public interface IStudentRepository
{
List<Student> List(); Student Get(int id); bool Add(Student student); bool Update(Student student); bool Delete(int id);
} public class StudentRepository : IStudentRepository
{
private static List<Student> Students = new List<Student> {
new Student{ Id=1, Name="小红", Age=10, Class="1班", Sex="女"},
new Student{ Id=2, Name="小明", Age=11, Class="2班", Sex="男"},
new Student{ Id=3, Name="小强", Age=12, Class="3班", Sex="男"}
}; public bool Add(Student student)
{
Students.Add(student); return true;
} public bool Delete(int id)
{
var stu = Students.FirstOrDefault(s => s.Id == id);
if (stu != null)
{
Students.Remove(stu);
} return true;
} public Student Get(int id)
{
return Students.FirstOrDefault(s=>s.Id == id);
} public List<Student> List()
{
return Students;
} public bool Update(Student student)
{
var stu = Students.FirstOrDefault(s=>s.Id == student.Id);
if (stu != null)
{
Students.Remove(stu);
} Students.Add(student);
return true;
}
}

我们新建了一个IRepository接口,里面有几个基本的crud的方法。然后新建一个实现类,并且使用静态变量保存数据,模拟数据持久化。

当然还得在DI容器中注册一下:

  public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
//注册repository
services.AddScoped<IStudentRepository, StudentRepository>();
}

实现列表(student/list)页面

列表页面用来展现所有的学生信息。

修改ListModel类:

    public class ListModel : PageModel
{
private readonly IStudentRepository _studentRepository;
public List<Student> Students { get; set; }
public ListModel(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
} public void OnGet()
{
Students = _studentRepository.List();
}
}

修改List.cshtml模板:

@page
@model RazorPageCRUD.ListModel
@{
ViewData["Title"] = "List";
} <h1>List</h1> <p>
<a class="btn btn-primary" asp-page="Add">Add</a>
</p>
<table class="table">
<tr>
<th>Id</th>
<th>Name</th>
<th>Age</th>
<th>Class</th>
<th>Sex</th>
<th></th>
</tr>
@foreach (var student in Model.Students)
{
<tr>
<td>@student.Id</td>
<td>@student.Name</td>
<td>@student.Age</td>
<td>@student.Class</td>
<td>@student.Sex</td>
<td>
<a class="btn btn-primary" asp-page="Update" asp-route-id="@student.Id">Update</a>
<a class="btn btn-danger" href="/student/delete?id=@student.Id" >Delete</a>
</td>
</tr>
} </table>

ListModel类混合了MVC的Controller跟Model的概念。它本身可以认为是MVC里面的那个Model,它包含的数据可以被razor试图引擎使用,用来生成html,比如它的Students属性;但是它又包含方法,可以用来处理业务逻辑,这个方法可以认为是Controller中的Action。方法通过特殊的前缀来跟前端的请求做绑定,比如OnGet方法就是对Get请求作出响应,OnPost则是对Post请求作出响应。

运行一下并且访问/student/list:



列表页面可以正常运行了。

使用asp-page进行页面间导航

列表页面上有几个按钮,比如新增、删除等,点击的时候希望跳转至不同的页面,可以使用asp-page属性来实现。asp-page属性不是html自带的属性,显然这是Razor Pages为我们提供的。

<p>
<a class="btn btn-primary" asp-page="Add">Add</a>
</p>

上面的代码在a元素上添加了asp-page="Add",表示点击这个a连接会跳转至同级目录的Add页面。html页面之间的导航不管框架怎么封装无非就是url之间的跳转。显然这里asp-page最后会翻译成一个url,看看生成的页面源码:

<a class="btn btn-primary" href="/Student/Add">Add</a>

跟我们想的一样,最后asp-page被翻译成了href="/Student/Add"。

使用asp-route-xxx进行传参

页面间光导航还不够,更多的时候我们还需要进行页面间的传参。比如我们的更新按钮,需要跳转至Update页面并且传递一个id过去。

<a class="btn btn-primary" asp-page="Update" asp-route-id="@student.Id">Update</a>

我们使用asp-route-id来进行传参。像这里的a元素进行传参,无非是放到url的querystring上。让我们看一下生成的html源码:

<a class="btn btn-primary" href="/Student/Update?id=2">Update</a>

不出所料最后id作为queryString被组装到了url上。

上面演示了Razor Pages的导航跟传参,使用了几个框架内置的属性,但其实我们根本可以不用这些东西就可以完成,使用标准的html方式来完成,比如删除按钮:

<a class="btn btn-danger" href="/student/delete?id=@student.Id" >Delete</a>

上面的写法完全可以工作,并且更加清晰明了,谁看了都知道是啥意思。

小小的吐槽下微软:像asp-page这种封装我是不太喜欢的,因为它掩盖了html、http工作的本质原理。这样会造成很多同学知道使用asp-page怎么写,但是换个框架就不知道怎么搞了。我见过号称精通asp.net的同学,但是对html、特别是对http一无所知。当你了解了真相后,甭管你用什么技术,看起来其实都是一样的,都是套路。

实现新增(student/add)页面

新增页面提供几个输入框输入学生信息,并且可以提交到后台。

修改AddModel类:

   public class AddModel : PageModel
{
private readonly IStudentRepository _studentRepository;
public AddModel(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public void OnGet()
{
} [BindProperty]
public Student Student { get; set; } public IActionResult OnPostSave()
{
_studentRepository.Add(Student);
return RedirectToPage("List");
}
}

修改Add.cshtml页面

@page
@model RazorPageCRUD.AddModel
@{
ViewData["Title"] = "Add";
} <h1>Add</h1> <form method="post">
<div class="form-group">
<label>Id</label>
<input type="number" asp-for="Student.Id" class="form-control" />
</div>
<div class="form-group">
<label>Name</label>
<input type="text" asp-for="Student.Name" class="form-control" />
</div>
<div class="form-group">
<label>Age</label>
<input type="number" asp-for="Student.Age" class="form-control" />
</div>
<div class="form-group">
<label>Class</label>
<input type="text" asp-for="Student.Class" class="form-control" />
</div>
<div class="form-group">
<label>Sex</label>
<input type="text" asp-for="Student.Sex" class="form-control" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" asp-page-handler="Save">Save</button>
<a asp-page="list" class="btn btn-dark">Cancel</a>
</div>
</form>

Add页面使用一个form表单作为容器,里面的文本框使用asp-for跟Model的Student属性建立联系。

运行一下:



asp-for会把关联的属性字段的值作为input元素的value的值,会把关联的属性名+字段的名称作为input元素的name属性的值。看看生成的html源码:

<input type="text" class="form-control" id="Student_Name" name="Student.Name" value="">

使用asp-page-handler来映射模型方法

我们的Save是一次POST提交,显然我们需要一个后台方法来接受这次请求并处理它。使用asp-page-handler="Save"可以跟模型的OnPostSave方法做映射。OnPost前缀表示对POST请求做响应,这又有点像webapi。那么asp-page-handler为什么能映射模型的方法呢?继续看看生成的源码:

<button type="submit" class="btn btn-primary" formaction="/Student/Add?handler=Save">Save</button>

看到这里就明白了。最后生成的button上有个formaction属性,值为/Student/Add?handler=Save。formaction相当于在form元素上指定action属性的提交地址,并且在url上附带了一个参数handler=save,这样后台就能查找具体要执行哪个方法了。不过据我的经验formaction属性存在浏览器兼容问题。

使用BindPropertyAttribute进行参数绑定

光能映射后台方法还不够,我们还需要把前端的数据提交到后台,并且拿到它。这里可以使用BindPropertyAttribute来自动完成提交的表单数据跟模型属性之间的映射。这样我们的方法可以是无参的方法。


[BindProperty]
public Student Student { get; set; }

看到这里突然有种MVVM模式的既视感了。虽然不是实时的双向绑定,但是也实现了简单的前后端绑定技术。另外提一句既然我们前端的数据是通过表单提交,那么跟mvc一样,使用FromFormAttribute其实一样可以进行参数绑定的。

public IActionResult OnPostSave([FromForm] Stuend student)

这有获取表单数据毫无问题。

在后台方法进行页面导航

当保存成功后需要使页面跳转到列表页面,可以使用RedirectToPage等方法进行跳转,OnPostSave方法的返回值类型也改成IActionResult,这就非常mvc了,跟action方法一模一样的套路。

public IActionResult OnPostSave()
{
_studentRepository.Add(Student);
return RedirectToPage("List");
}

修改编辑(student/update)页面

修改,删除页面就没什么好多讲的了,使用前面的知识点轻松就能实现。

修改cshtml模板:

@page
@model RazorPageCRUD.UpdateModel
@{
ViewData["Title"] = "Update";
} <h1>Update</h1> <form method="post">
<div class="form-group">
<label>Id</label>
<input type="number" asp-for="Student.Id" class="form-control" />
</div>
<div class="form-group">
<label>Name</label>
<input type="text" asp-for="Student.Name" class="form-control" />
</div>
<div class="form-group">
<label>Age</label>
<input type="number" asp-for="Student.Age" class="form-control" />
</div>
<div class="form-group">
<label>Class</label>
<input type="text" asp-for="Student.Class" class="form-control" />
</div>
<div class="form-group">
<label>Sex</label>
<input type="text" asp-for="Student.Sex" class="form-control" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" asp-page-handler="Edit">Save</button>
<a asp-page="list" class="btn btn-dark">Cancel</a>
</div>
</form>

修改UpdateModel类:

    public class UpdateModel : PageModel
{
private readonly IStudentRepository _studentRepository;
public UpdateModel(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public void OnGet(int id)
{
Student = _studentRepository.Get(id);
} [BindProperty]
public Student Student { get; set; } public IActionResult OnPostEdit()
{
_studentRepository.Update(Student); return RedirectToPage("List");
}
}

运行一下:

修改删除(student/delete)页面

删除页面跟前面一样没什么好多讲的了,使用前面的知识点轻松就能实现。

修改Delete.cshtml模板:

@page
@model RazorPageCRUD.DeleteModel
@{
ViewData["Title"] = "Delete";
} <h1>Delete</h1>
<h2 class="text-danger">
确定删除?
</h2>
<form method="post">
<div class="form-group">
Id: @Model.Student.Id
</div>
<div class="form-group">
Name:@Model.Student.Name
</div>
<div class="form-group">
Age: @Model.Student.Age
</div>
<div class="form-group">
Class: @Model.Student.Class
</div>
<div class="form-group">
Sex: @Model.Student.Sex
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" asp-page-handler="Delete" asp-route-id="@Model.Student.Id">Delete</button>
<a asp-page="list" class="btn btn-dark">Cancel</a>
</div>
</form>

修改DeleteModel类:

     public class DeleteModel : PageModel
{
private readonly IStudentRepository _studentRepository;
public DeleteModel(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
} public void OnGet(int id)
{
Student = _studentRepository.Get(id);
} public Student Student { get; set; } public IActionResult OnPostDelete(int id)
{
_studentRepository.Delete(id); return RedirectToPage("list");
}
}

运行一下:

总结

通过上的简单示例,对Razor Pages有了大概的了解。Razor Pages本质上对MVC模式的简化,后台模型聚合了Controller跟Model的的概念。并且提供了一些内置html属性实现绑定技术。有人说Razor Pages是WebForm的继任者,我倒不觉得。个人觉得它更像是MVC/MVVM的一种混合。[BindProperty]有点像WPF里的依赖属性,OnPostXXX方法就像是Command命令;又或者[BindProperty]像VUE的Data属性上的字段,OnPostXXX像Methods里的方法;又或者整个Model像极了angularjs的$scope,混合了数据跟方法。只是Razor Pages毕竟是服务端渲染,不能进行实时双向绑定而已。最后,说实话通过简单的体验,Razor Pages开发模式跟MVC模式相比并未有什么特殊的优点,不知道后续发展会如何。

ASP.NET Core Razor Pages 初探的更多相关文章

  1. ASP.NET Core Razor Pages

    Razor 页面是Asp.Net Core2.0新增的一个功能.Razor 页面是 ASP.NET Core MVC 的一个新特性,它可以使基于页面的编码方式更简单高效. 环境:vs2017 .net ...

  2. ASP.NET Core - Razor页面之Handlers处理方法

    简介 在前一篇文章中,我们讨论了Razor页面.今天我们来谈谈处理方法(Handlers). 我们知道可以将代码和模型放在 .cshtml 文件里面或与 .cshtml 匹配的 .cshtml.cs ...

  3. ASP.NET Core:Pages

    ylbtech-ASP.NET Core:Pages 1.返回顶部 1._Layout.cshtm <!DOCTYPE html> <html> <head> &l ...

  4. .NET Core Razor Pages中ajax get和post的使用

    ASP.NET Core Razor Pages Web项目大部分情况下使用继承与PageModel中的方法直接调用就可以(asp-page),但是有些时候需要使用ajax调用,更方便些.那么如何使用 ...

  5. ASP.NET Core - Razor 页面简介

    简介 随着ASP.NET Core 2 即将来临,最热门的新事物是Razor页面.在之前的一篇文章中,我们简要介绍了ASP.NET Core Razor 页面. Razor页面是ASP.NET Cor ...

  6. ASP.NET Core Razor中处理Ajax请求

    如何ASP.NET Core Razor中处理Ajax请求 在ASP.NET Core Razor(以下简称Razor)刚出来的时候,看了一下官方的文档,一直没怎么用过.今天闲来无事,准备用Rozor ...

  7. 学习ASP.NET Core Razor 编程系列一

    一. 概述 .NET Core 1.0发布的时候就想进行学习的,不过根据微软的以往的发布规律1.0版可以认为是大众测试版,2.0才算稳定.现在2.1都已经发布了预览版,之前对其"不稳定&qu ...

  8. 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  9. 学习ASP.NET Core Razor 编程系列五——Asp.Net Core Razor新建模板页面

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

随机推荐

  1. 【Pytest02】全网最全最新的Pytest框架快速进阶篇(pytest前置和后置以及忽略测试用例)

    一.Pytest的前置和后置方法 1.Pytest可以集成unittest实现前置和后置 import unittest import pytest class TestCase(unittest.T ...

  2. return console.log()结果为undefined现象的解答

    console.log总是出现undefined--麻烦的console //本文为作者自己思考后总结出的一些理论知识,若有错误,欢迎指出 bug出现 ​ 需求如下:新建一个car对象,调用其中的de ...

  3. Hystrix 使用手册 | 官方文档翻译

    由于时间关系可能还没有翻译全,但重要部分已基本包含 本人水平有限,如有翻译不当,请多多批评指出,我一定会修正,谢谢大家.有关 ObservableHystrixCommand 我有的部分选择性忽略了, ...

  4. 【3D】PoseCNN姿态检测网络复现过程记录

    最近在研究室内6D姿态检测相关问题,计划在PoseCNN网络基础上进行改进实现.但是在第一步的复现过程中踩了无数的坑,最终成功运行了demo,但目前数据集train还是遇到了一些问题.有问题欢迎一起交 ...

  5. JAVA实现图片验证

    一.什么是图片验证码? 可以参考下面这张图: 我们在一些网站登陆的时候,经常需要填写以上图片的信息. 这种图片验证方式是我们最常见的形式,它可以有效的防范恶意攻击者采用恶意工具,来进行窃取用户的密码 ...

  6. Vertica的这些事(四)——-vertica加密数据

    通过创建 Secure Access Policies可以对vertica中的某一列数据进行加密: CREATE ACCESS POLICY ON [schema][tablename] FOR CO ...

  7. PTA数据结构与算法题目集(中文) 7-15

    PTA数据结构与算法题目集(中文)  7-15 7-15 QQ帐户的申请与登陆 (25 分)   实现QQ新帐户申请和老帐户登陆的简化版功能.最大挑战是:据说现在的QQ号码已经有10位数了. 输入格式 ...

  8. 怎样用scratch2.0谱写音乐

    打开scratch2.0将语言切换为简体中文: 如果需要播放特殊的声音,可以用播放声音,找到一些特有的音乐,或者通过录制,将自己的配音或者唱歌录制下来: 可以用弹奏鼓声命令弹奏各种击鼓音乐: 通过控制 ...

  9. 记一次mysql多表查询(left jion)优化案例

    一次mysql多表查询(left jion)优化案例 在新上线的供需模块中,发现某一个查询按钮点击后,出不来结果,找到该按钮对应sql手动执行,发现需要20-30秒才能出结果,所以服务端程序判断超时, ...

  10. Matlab入门(一)

    1.常用命令 cd 显示或改变当前工作目录 load 加载指定文件的变量 dir 显示当前目录或指定目录下的文件 diary 日志文件命令 clc 清除工作窗中的所有显示内容 ! 调用 DOS 命令 ...