上周期待已久的 Asp.Net Core 2.0 提前发布了,一下子 Net 圈热闹了起来,2.0 带来了很多新的特性和新的功能,其中 Razor Page 引起我的关注,作为 web 程序员来说,Asp.Net 下的任何 web 框架都会去特别关注,因为每次一个新的框架出来,意味着一次革命。此次的 Razor Page 是否能带来不一样的体验呢,让我们一起来看看吧。
我们都知道在 Asp.Net MVC 中,Razor 是其一种视图引擎。而今天我们介绍的 Razor Page 却是一种 web 框架,它是一种简化的 MVC 框架,如果你曾经做过 WebForm 的开发者,你会发现,Razor Page 有点类似 Web Form,一个 page,一个 class。
大家或许会有疑惑,我们现在 Asp.Net MVC 已经很完善了,为何还需要出来一种新型的框架呢?在我看来,MVC 确实已经足够强大了,只是因为太强大了,却变成了它的缺点。当我们的业务越来越庞大的时候,你是否觉得你的一个 Controller 内部已经凌乱不堪?当我们业务模块划分越多的时候,你是否会为你的 Model 创建而头疼呢?当我们创建一个新的 View 的时候,我们需要在 MVC 层增加 1 个 View,1 个 Model,修改一个 Controller,每当这个时候,我都会疑惑这不是违反 Open-Closed Principle(对扩展开放,对修改关闭)了嘛!这个时候我会想起以前的 webform,现在不需要了,我们有了 Razor Page,一种更轻量级的 MVC(我觉得更像 MVVM)。
我们可以通过多种方式来创建 Razor Page 项目,最简单的就是利用 dotnet 命令方式,当然我还是建议您使用 Visual Studio 2017(宇宙最强的 IDE)。要创建 Razor Page,你需要先安装. Net Core 2.0 SDK,如果要使用 VS2017 来创建,您还必须要更新到 15.3 版本以上
打开 cmd 或者 powershell 工具,先检查下你的 dotnet 版本是否为 2.0.0
dotnet –version
先通过命令,到你需要创建项目的目录,我这里为 E 盘下 demos 目录:cd e:\demos\RazorPageDemo1
- dotnet new razor
输入以上命令,你就已经创建了 razorPage 的项目了,这里说一下 dotnet 2.0 默认是自动 restore 的,你也可以通过 --no-restore 选项关闭。我们直接通过命令 dotnet run 可以直接运行,看到的页面应该跟之前 mvc 创建的类似。
输入 dir,我们看下生成了哪些:
跟之前 mvc 不同的是,我们不再看到 model,view,controller 目录了,取而代之的是 Pages 目录,这个就是我们的 razor Page 的主要工作目录。
用 Visual Studio 2017 创建是非常方便的(宇宙最强 IDE),不过我们必须要先升级到 15.3,升级之后选择新建项目 ->.Net Core –> Asp.Net Core Web 应用程序,接下来会弹出一个对话框,让我们选择模板类型:
多了好多模板,好兴奋啊!我们在这里无法找到 Razor Page,那是因为 Razor Page 已经变成默认的【Web 应用程序模板】了,而传统的 MVC 方式已经变成【Web 应用程序(模型视图控制器)】。选择【Web 应用程序模板】,点击确定我们就完成创建了,通过 Solution Explore,我们可以看到:
与命令方式创建的一致。
通过上节我们创建了 Razor Page 项目,直接通过 dotnet run 或者在 vs 中 F5 运行。上文中我们说到,Razor Page 的项目中,我们的关注点都在 Pages 目录下,在 VS Explore 中,我们看到在 Index.cshtml 的左边有一个三角箭头,点击就会看到 Index.cshtml.cs 文件,是不是感觉回到了 webform。我们看下代码:
- public class IndexModel : PageModel
- {
- public void OnGet()
- {
- }
- }
因为我们的 Index 页面没有绑定任何数据,所以这里基本上只继承了 PageModel,OnGet 方法是个约定,查看 mvc 的源码你会发现它会获取 On{handler}{Async}()。比如 OnGet,它会在 Get Index 的时候被执行,我们可以通过这个约定进行数据绑定,这里知道下在 Razor Page 下 HttpMethod 也是一个 handler,所以 Razor Page 的处理方式是通过 handler 进行的。
举个例子,我们在 IndexModel 中添加一个 String 类型的属性 Message,在 OnGet 中进行赋值:
- public void OnGet()
- {
- Message = "this is a test!";
- }
然后我们修改下 Index.csthml:
- @page
- @model IndexModel
- @{
- ViewData["Title"] = "Home page";
- }
- <div class="row">
- Message : @Model.Message
- </div>
运行下,如果我们在页面上看到 Message : this is a test!,说明赋值成功。
是不是很方便,一般我们的 web 基本上百分之八十在 Get 和 Post,特别情况会出现其他 HttpMethod,当然我们的 RazorPage 也支持,不过不建议。
现在来说 PageModel 就是一个 Model,Action,HttpMethod 的合体,对于 Controller 使用文件自己的路径 + 文件名的方式,比如原先我们的 HomeController,默认情况下我们可以通过'/'访问也可以通过'/Home/' 访问,这其实有歧义的,为了避免这种情况,我们必须去修改 Route,非常不方便,而现在,我们只需要在 Pages 主目录下创建相应的 Action 就可以了,微软提供了 Razor Page 的对应 Url 关系,如图:
你是否会问现在还支持 / Controller/Action/ID 吗?
支持,不过你需要在 cshtml 页面上,通过 @page 设置路由
@page "{parameter:type?}"
例如 /Address/Province/City 我们只需要在 Address/Index.cshtml 页面上加入如下:
@page "{Province}/{City?}"
问号代表可选参数。这样的好处就是我们不需要在 RegisterRoute 的时候去填写规则了,是不是很棒!
那像原来我们在一个 Controller 中,有 Get() 和 Get(id) 表示获取列表和获取单个 Item,那在 Razor Page 中如何运用呢?
抱歉,目前我没有找到最佳的解决方法,原本我打算在 @page "~/user/{id:int}",但是测试结果发现不支持,因为我们的 page 对应到 url 也是一个目录,@page route 的时候它不会识别绝对路径和相对路径,它只会在当前路径后面添加映射,也就是说我们的 url 变成了 / users/user/{id},目前最佳的解决方式是建立两个目录,如下:
在 Razor Page 中,数据绑定是非常简单的, 您只要在需要绑定的属性上添加 [BindProperty] 特性即可。
- public class IndexModel : PageModel
- {
- public string Message { get; set; }
- [BindProperty]
- public User TestUser { get; set; }
- }
- public class User
- {
- public Guid UserID { get; set; }
- public string Name { get; set; }
- }
默认情况模型绑定不支持 Get 方法,你需要使用 [BindProperty(SupportsGet=true)]
TempData 是 Asp.Net Core 2.0 新增的特性,你只需要在 PageModel 中的属性上加上 TempData 特性即可。加上 TempData 特性的属性,会在你跳转路由或者页面的时候隐性的传递过去。
什么意思呢?比如当你创建一个用户的时候,你会希望跳转回用户列表页,并在用户列表页提示添加成功的信息,这时候你可以通过在 Message 属性上加上 [TempData] 特性,引用下微软 Docs 的例子:
- public class CreateDotModel: PageModel {
- [TempData] public string Message {
- get;
- set;
- }
- [BindProperty] public Customer Customer {
- get;
- set;
- }
- public async Task < IActionResult > OnPostAsync() {
- if (!ModelState.IsValid) {
- return Page();
- }
- //todo create a new customer
- Message = $ "Customer {Customer.Name} added";
- return RedirectToPage("./Index");
- }
- }
跳转到 Index 后,我们的 IndexModel 的 Message 属性(需要同样设置 TempData 特性)就会被赋值。有点类似于之前的 model 传递,但又不一样,感觉棒棒哒!
Q: 自定义 routing 的时候,无法支持绝对路径和相对路径
A: 应该可以通过重写某个接口达到目的,稍后我会看下
Q: 不支持多个 handler 在同一个 pageModel 中,比如 OnGet, OnGetAsync 不能在同一个 PageModel 中
A:可以通过自己重写 IPageHandlerMethodSelector 接口,然后注册到 service 中应该可以解决。
Q: 用 VS2017 创建新的 Page 的时候,会在页面上显示红线
A:关闭页面再打开。。。。
最近工作有点忙,Core2.0 的出现使 Net 圈沸腾了,RazorPage 的出现更是让我们这种 web 开发者为之振奋,今天介绍的有限,毕竟也是刚出来的东西。个人觉得 Razor Page 还是非常棒的,虽然还有些问题,如果遇到 Razor Page 无法解决的事情,请大家结合 MVC,国外有大神就是这么做的,但我相信不久之后,Razor Page 会疯狂出现在我们面前,特别是对于微服务架构来说,简单和快速是微服务的重要所在。
最后推荐下自己的. Net Core 学习群:376248054
来源: http://www.cnblogs.com/inday/p/razor-page-in-asp-net-core-2.html