Razor Page介紹前言 上周期待已久的Asp.Net Core 2.0提前發佈了,一下子Net圈熱鬧了起來,2.0帶來了很多新的特性和新的功能,其中Razor Page引起我的關註,作為web程式員來說,Asp.Net下的任何web框架都會去特別關註,因為每次一個新的框架出來,意味著一次革命。... ...
Razor Page介紹
前言
上周期待已久的Asp.Net Core 2.0提前發佈了,一下子Net圈熱鬧了起來,2.0帶來了很多新的特性和新的功能,其中Razor Page引起我的關註,作為web程式員來說,Asp.Net下的任何web框架都會去特別關註,因為每次一個新的框架出來,意味著一次革命。此次的Razor Page是否能帶來不一樣的體驗呢,讓我們一起來看看吧。
什麼是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
我們可以通過多種方式來創建Razor Page項目,最簡單的就是利用dotnet命令方式,當然我還是建議您使用Visual Studio 2017(宇宙最強的IDE)。要創建Razor Page,你需要先安裝.Net Core 2.0 SDK,如果要使用VS2017來創建,您還必須要更新到15.3版本以上
dotnet命令方式創建
打開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創建Razor Page
用Visual Studio 2017創建是非常方便的(宇宙最強IDE),不過我們必須要先升級到15.3,升級之後選擇新建項目->.Net Core –> Asp.Net Core Web應用程式,接下來會彈出一個對話框,讓我們選擇模板類型:
多了好多模板,好興奮啊!我們在這裡無法找到Razor Page,那是因為Razor Page已經變成預設的【Web應用程式模板】了,而傳統的MVC方式已經變成【Web應用程式(模型視圖控制器)】。選擇【Web應用程式模板】,點擊確定我們就完成創建了,通過Solution Explore,我們可以看到:
與命令方式創建的一致。
QuickStart Razor Page
Hello Razor Page
通過上節我們創建了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關係,如圖:
快速自定義Routing
你是否會問現在還支持/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 臨時數據
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