【复杂系统迁移 .NET Core平台系列】之界面层
源寶導讀:微軟跨平臺技術框架—.NET Core已經日趨成熟,已經具備了支撐大型系統穩定運行的條件。本文將介紹明源云ERP平臺從.NET Framework向.NET Core遷移過程中的實踐經驗。
一、背景
? ? 隨著ERP的產品線越來越多,業務關聯也日益復雜,應用間依賴關系也變得錯綜復雜,單體架構的弱點日趨明顯。19年初,由于平臺底層支持了分應用部署模式,將ERP從應用子系統層面進行了切割分離,邁出了從單體架構向微服務架構轉型的堅實一步。不久的將來,ERP會進一步將各業務拆分成眾多的微服務,而微服務勢必需要進行容器化部署和運行管理,這就要求ERP技術底層必須支持跨平臺,所以將現有ERP系統從.NET Framework遷移到 .NET Core平臺勢在必行。
? ? 上一篇《【復雜系統遷移 .NET Core平臺系列】之遷移項目工程》的文章中,我們分享了在改造.NET Core工程和類庫過程中的經驗,本文我們將分享在界面層改造過程中遇到的問題和解決思路。
? ? ERP目前使用的是Asp.Net WebForm框架,在.NET Core中已經被廢棄了,那么這部分就只有采用重寫的方式。在Asp.Net Core Mvc中RazorPage的訪問方式跟WebForm基于物理路徑的訪問提供了類似的機制;并且PageModel也跟Aspx的CodeBehind的方式類似。就是基于這樣類似的方式,提供了結構和邏輯復用的可能性。基于這個起點,本篇將從如下步驟講述界面層的改造過程:
頁面路由:改造Url重定向的邏輯,定位到頁面;
Aspx頁面:改造Aspx頁面為cshtml頁面;
模板頁面:改造Master為Layout頁面;
服務端控件:改造服務端用戶Ascx頁面。
二、頁面路由改造
? ? ERP的頁面有兩種,一種是由建模配置出來的叫標準頁面,由平臺托管來提供功能;另一種是自定義頁面,自己寫Aspx實現所有業務邏輯。標準頁面使用Url重寫,而自定義頁面直接用路徑訪問。在Asp.Net Core MVC中從Url映射到RazorPage是通過路由模塊來實現的,為了支持以前的訪問Url不變,我們需要對路由進行擴展。
? ? 區別于傳統Asp.Net Mvc的頁面通過路徑加載,然后運行時編譯不同,在.NET Core中默認是直接將*.cshtml頁面編譯到DLL中,然后通過AssemblyPart加載到MVC框架中,默認RazorPage的路徑是DLL中相對于Page文件夾的路徑。
? ? 例如下圖所示的頁面訪問路徑為“/PubPlatform/Nav/Home/Default”:
1、標準頁面路由
? ?舉一個平臺的標準頁面Url的例子:“/std/00002212/822952a2-5091-e711-9bda-001583b5bd1a.aspx”。這個Url可以通過HttpModule重寫為:
“/_frontend/templates/pages/Grid/template.aspx?FunctionCode=00002212&_pagename=822952a2-5091-e711-9bda-001583b5bd1a”。重寫主要目的是找到“822952a2-5091-e711-9bda-001583b5bd1a”這個ID對應頁面元數據內容,然后通過配置映射到物理路徑的template.aspx頁面。
?RazorPage是通過IPageRouteModel Convention來提供路由的擴展的,提供了PageRouteModelConvention和FolderRoute ModelConvention類來實現擴展,這兩種使用方式可以通過內置的AddPageRoute和AddFolderRouteModelConvention方法來實現,下面是平臺使用文件夾路由轉換的代碼示例:
? ? 在上述代碼中我們為所有/templates/pages路徑下的頁面,添加三個路由參數:
AppConst.FunctionCodeKey(FucntionCode):模塊代碼 對應上述Url中的00002212;
AppConst.PageNameKey(PageName):元數據標識 對應上述Url中的822952a2-5091-e711-9bda-001583b5bd1a;
AppConst.PageKey(Page):RazorPage頁面的相對于Page文件夾的路徑(框架自帶)。
? ? TemplateConstraint實現IActionConstraint用于在路由系統中決定URL是否匹配當前頁面,還是仿照之前HttpModule中Url重寫邏輯來做路由匹配即可實現Url路由到對應的RazorPage。
2、自定義頁面路由
? ? 我們先看看改造前和改造后頁面的組織結構對比:
? 在改造自定義頁面路由的時候我們使用IPageRouteModelProvider擴展OnProviders Executing,擴展代碼如下:
? ? 在標準頁面中我們使用了MVC自帶的擴展機制,自定義頁面我們使用IPageRoute ModelProvider擴展OnProvidersExecuting,因為在標準頁面中我們使用路徑來做匹配,但是自定義頁面中需要兼容產品頁面路徑和項目頁面路徑,使用了類似于黑名單和白名單的區別,所以分別采用了不同的策略。另外這里我們擴展了OnProvidersExecuted的方法,在所有路由加載完之后來進行路由替換,來實現項目頁面替換產品頁面。
三、頁面改造
? ? 解決了路由訪問的問題,接下來就要進行Aspx頁面改造成cshtml了。首先我們簡單介紹下RazorPage的一些基本特性:
前端通過cshmtl來渲染Html;
后端通過PageModel來處理業務邏輯;
通過OnGet或者OnPost來提供訪問;
通過PageModel屬性和BindProperty來實現參數到模型的綁定;
通過PageModel屬性實現PageModel到cshtml頁面的數據傳遞。
? ? ERP所有的Aspx都沒有*.cs的后臺代碼。這里簡單介紹下ERP對Aspx頁面的使用規范:
實現Html渲染邏輯;
OnLoad方法重寫實現取數等簡單后臺操作;
頁面回傳請求通過Ajax處理 從功能特性可以發現我們不用處理傳統Aspx的回傳的請求。Aspx的OnLoad可以使用RazorPag的OnGet方法中來替換,然后賦值給PageModel的屬性來傳遞給cshtml來綁定數據,頁面中只需要將Aspx語法替換成Razor的語法即可。
? ? 語法轉換工具可以從這里找到:https://github.com/telerik/razor-converter。
? ? 在之前ERP中由于缺少了繼承自Page的基類所以在頁面中會有重復代碼,在新版本改造中我們將一些通用方法進行了抽取,做了如下的繼承層級的改造:
? ? 有了這一層之后就能對一些例如權限、多語言、多時區等進行通用代碼的封裝。并且為以后通用邏輯等定義一個可擴展的地方。但是封裝之后還有一個問題如果每個頁面都需要去設置繼承關系就會顯得有些重復,并且容易犯錯,在MVC中提供了如下兩種簡潔的方式:
_ViewStart中定義的邏輯全部頁面生效;
_ViewImport按照目錄優先級,路徑靠近_ViewImport會重寫上層的邏輯,對下層的頁面生效。
四、模板頁面改造
? ? 我們在做頁面的時候一般都是需要使用Master頁面來做布局封裝的,首先我們來一張看看平臺Aspx和cshtml的布局層級對比圖 :
1、頂層Layout頁面
? ? 原來的Master頁面中有大量的重復代碼,所以這里做了一層抽象。頂層Layout頁面用來定義Html結構,通常html中將css部分放在header中,將script部分放在body的最下面。另外css和js分別可以分類為,第三方css/js,通用css/js,文件引用的css/js,系統自定義輸出到界面的css/js和用戶自定義的css和js,這些都是通過MVC的RenderSection方法,提供類似與類中virutal方法一樣可以由頁面重寫。下面是示例代碼:
2、模板頁面
? ? Nav模板頁面定義Body結構,包括不同的頁面布局:
_Nav.cshtml 用來定義帶導航的頁面;
_Json.cshtml 用來定義json頁面;
_Content.cshtml 用來定義無導航的內容頁面;
_Dialog.cshtml 用來定義彈框頁面。
3、模板頁面設置
? ? 在ERP中因為標準頁面可以掛載到不同的模板中,例如常見的表單頁面可以加載到帶導航的頁面中,也可以加載到彈出框中。而且指定模板的方式不是通過直接跟路徑關聯.由于使用了預編譯的機制,并且頁面的路徑希望對其他引用者屏蔽,所以用名字代替頁面路徑來設置模板,從而讓模板頁面路徑和使用者解耦。通過如下邏輯來替換:
在自定義頁面中我們直接使用名字指定;
在標準建模頁面中我們使用元數據中配置的名字來指定;
在url中通過_mp參數來指定模板的名字,兼容一些圖書兼容場景,例如頁面嵌套的業務場景;
最終為了兼容一套名稱和路徑對應關系,兼容Framework和Core我們使用了類似如下的映射方式。
五、服務端控件ascx改造
? ? 在Aspx的使用中為了提高代碼的復用程度,一般都會使用Ascx的自定義控件,在MVC中有多種的替代方式,在比較單純的數據綁定頁面中我們使用了PartialView來替代,這部分主要在布局頁面中使用,有如下模板:
Collapse.cshtml分類組件頁面,默認收起左側導航;
Footer.cshtml 底部組件,用于返回頂部;
Holder.cshtml 內容部件;
Navbar.cshtml 頂部導航組件,用于顯示用戶相關設置通知等信息;
Sidebar.cshtml 用于顯示左邊菜單。
? ? 為了將CSS,JS等資源加載更加集中化,我們也將布局頁面中一些資源按照功能加載進行了加載。如下:
模塊化sea.js;
默認的一些輸出到界面的js變量,如用戶名,時區,頁面元數據等;
權限事件腳本等,(之前這部分可能會放在后臺cs代碼中,現在也放在部分頁里面加載)。
? ? 除了這些簡單邏輯之外,還有一些組件會直接使用后臺的服務取數據,然后渲染頁面這部分使用Core的Component(視圖組件)來加載 例如tab頁面的加載,這里需要取得上下文中用戶信息,從而獲取權限然后判斷是否加載。
? ? 在標準頁面中會用到列表,樹列表等大控件,這類控件的邏輯在后臺配合RazorEngine來渲染的,這部分對Asp.NET Core MVC正好兼容,在原來是通過自定義控件來提供訪問,在Asp.NET MVC中是通過Html.XXXFor類似方法提供訪問,在Core中為了讓cshtml可讀性更好,提供了TagHelper的方式,這里也將訪問方式通過了TagHelper封裝,代碼示例:
六、總結
? ??在Framework中有很多前端代碼和后端代碼糅合在一個Aspx頁面中。有了RazorPage之后可以用cshtml來渲染界面,使用PageModel來處理數據,這樣做到前端邏輯和后端邏輯的分離,并且根據職責將之前的不同功能的界面渲染需求,進行封裝和職責的分離,使前端渲染邏輯更加清晰也更容易維護。在下一篇分享中,我們將介紹針對頁面資源文件引用(例如CSS、JS文件)的遷移過程,大家敬請期待。
------ END ------
作者簡介
熊同學:?研發工程師,目前負責ERP運行平臺的設計與開發工作。
也許您還想看
【復雜系統遷移 .NET Core平臺系列】之遷移項目工程
.NET Core MVC擴展實踐
分布式鎖的實現與探索
ERP緩存實踐經驗分享
總結
以上是生活随笔為你收集整理的【复杂系统迁移 .NET Core平台系列】之界面层的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【复杂系统迁移 .NET Core平台系
- 下一篇: 解惑小微企业信息化系统上云的顾虑