最近新建 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. coding++:高并发解决方案限流技术-使用RateLimiter实现令牌桶限流-Demo

    RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率. 通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位时 ...

  2. hive的基本操作与应用

    通过hadoop上的hive完成WordCount 启动hadoop Hdfs上创建文件夹 创建文件夹 上传文件至hdfs 启动Hive 创建原始文档表 导入文件内容到表docs并查看 用HQL进行词 ...

  3. Hadoop调试记录(2)

    自从上次调通hbase后很久没有碰hadoop了,今日想写一个mapreduce的小程序.于是先运行了下自带的wordcount示例程序,却报错了. 信息如下: kevin@ubuntu:~/usr/ ...

  4. Java读源码之ReentrantLock

    前言 ReentrantLock 可重入锁,应该是除了 synchronized 关键字外用的最多的线程同步手段了,虽然JVM维护者疯狂优化 synchronized 使其已经拥有了很好的性能.但 R ...

  5. 对oracle里面clob字段里面xml的增删改查学习

    这段时间,我使用系统表里面有clob字段里面存放的xml信息,我们如何对xml进行增删改查操作呢,自己参考了很多也学到很多,给大家分享一下 首先我们先建测试表 CREATE TABLE EFGP_23 ...

  6. U - Inviting Friends HDU - 3244( LIS 最长升序子序列——变形 )

    序列变换 Problem Description 我们有一个数列A1,A2-An,你现在要求修改数量最少的元素,使得这个数列严格递增.其中无论是修改前还是修改后,每个元素都必须是整数. 请输出最少需要 ...

  7. 【Net】ABP框架学习之它并不那么好用

    前言 上一篇文章介绍了ABP的Web API,本文在继续介绍ABP的其他内容. 在ABP中,WEBAPI是一个值得用的东西.但其他东西,就不一定是那么好用了. 下面我们看一下ABP的Controlle ...

  8. Springboot项目中 前端展示本地图片

    Springboot项目中 前端展示本地图片 本文使用的是Springboot官方推荐的thymeleaf(一种页面模板技术) 首先在pom文件加依赖 <dependency> <g ...

  9. Appium自动化(2) - appium环境安装常见问题的解决方案

    如果你还想从头学起Appium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1693896.html 问题1:adb检测不到设备 解决 ...

  10. java类文件结构笔记

    注:新的博客地址 - https://zhengw-tech.com/archives/ 我们都知道java实现跨平台靠的是虚拟机技术,将源文件编译成与操作系统无关的,只有虚拟机能识别并执行的字节码文 ...