日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

【复杂系统迁移 .NET Core平台系列】之静态文件

發布時間:2023/12/4 windows 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【复杂系统迁移 .NET Core平台系列】之静态文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

源寶導讀:微軟跨平臺技術框架—.NET Core已經日趨成熟,已經具備了支撐大型系統穩定運行的條件。本文將介紹明源云ERP平臺從.NET Framework向.NET Core遷移過程中的實踐經驗。

一、背景

? ? 隨著ERP的產品線越來越多,業務關聯也日益復雜,應用間依賴關系也變得錯綜復雜,單體架構的弱點日趨明顯。19年初,由于平臺底層支持了分應用部署模式,將ERP從應用子系統層面進行了切割分離,邁出了從單體架構向微服務架構轉型的堅實一步。不久的將來,ERP會進一步將各業務拆分成眾多的微服務,而微服務勢必需要進行容器化部署和運行管理,這就要求ERP技術底層必須支持跨平臺,所以將現有ERP系統從.NET Framework遷移到 .NET Core平臺勢在必行。

? ? 上一篇我們講述了Erp在改造.Net?Core頁面的處理,這一篇我們將講述在靜態文件改造過程中遇到的問題和解決思路。

二、靜態文件擴展需求

? ? 在ERP中,不僅僅要支持后端的擴展需求,還要支撐前端的擴展,ERP常見的擴展就是項目擴展標準產品的功能,其中包含JS和CSS的擴展

sea.js的擴展

? ? ERP采用sea.js的同步js加載機制,sea.js中是通過sea-config.js的配置來通過模塊名映射到正確的js路徑,而ERP中sea-config.js因為屏蔽掉手動配置,所以采取動態生成機制,通過如下幾步完成前端sea-config.js的加載。

我們需要先了解下sea.js的編碼規范,以下是js文件的一個示例:

//OrganizationAppService.js文件 /** * AppService代理類 * @author 本代碼由代碼生成器自動生成,請不要手工調整 * http://localhost:4602/script/Mysoft.PubPlatform.Organization.AppServices.OrganizationAppService/proxy */ define("Mysoft.PubPlatform.Organization.AppServices.OrganizationAppService", function (require, exports, module) {var utility = require("utility");//業務邏輯代碼})

有了確定好的格式以后,我們會在編譯前端代碼時候掃描OrganizationAppService.js的路徑和 define中定義的模塊名,生成如下json結構:{ "Mysoft.PubPlatform.Organization.AppServices.OrganizationAppService": {"type": "product","product": {"path": "PubPlatform/Organization/AppService/OrganizationAppService.js","description": "\r\n\r\nAppService代理類\r\n@author本代碼由代碼生成器自動生成,請不要手工調整\r\nhttp:localhost:4602scriptMysoft.PubPlatform.Organization.AppServices.OrganizationAppServiceproxy\r\n\r\n","mtime": 1581492465639}} }

? ? 根據ERP的擴展要求,會有如下三個文件:? ??

  • 平臺js文件 _frontend_build\platform-module.json。

  • 產品js文件 _frontend_build\product-module.json。

  • 二開js文件 _frontend\Customize{app}\dist\sea-config.json(app代表系統簡稱例如cbxt)。

? ? 接下來就是在代碼中生成如下的sea-config.js內容,因為上述json內容是一個結構化的內容,通過程序反序列化加載如上三個文件到內存中,再轉換成如下標準的sea-config.js的標準內容:

var __lang = !!window.Mysoft ? window.Mysoft.Map6.UI.page.lang : ""; seajs.config({"vars": {"lang": "zh-cn"},"alias": {"RptUtility": "Report/Common/RptUtility.js","Mysoft.Report.Preview.RptDownload": "Report/Preview/RptDownload.js",},"map": [[/^.*$/, function (url) { return url + "?_t=31634695680000000&lang=" + __lang; }]],"base": "/","debug": true });

? ? 這樣就完成了整個js文件的sea.js的整個代碼編寫到配置加載的整個環路。最后在在Framework的ERP中是通過配置在webconfig的SeaConfigHandler進行加載的。

多語言js的擴展

? ? ERP的js支持多語言的擴展,而多語言都存儲在后端,所以js文件的加載是配置在webconfig中配置的StaticFileHandler來擴展功能,在加載中讀取物理文件內容時候,進行多語言替換之后返回到前端。

css文件擴展需求

? ? css文件加載為了產品進行二次開發擴展也是通過StaticFileHandler的配置來加載,首先加載產品的css文件,然后加載項目的css文件,將兩個文件內容進行合并后返回到前端。

靜態文件緩存的需求

? ? 在上述的靜態文件加載過程中,都增加了瀏覽器靜態文件緩存的功能,通過max-age 和etag的http頭進行標記,讓瀏覽器知道要緩存靜態文件,從而減少對后端服務器的請求數據。

? ? 上一篇文章中我們提到的TagHelper中,靜態文件也可以通過在生成的url中,拼接文件的的 SHA256的哈希值來直接使用瀏覽器自帶的緩存功能。

二、.Net Core靜態文件加載原理

? ? 前面我們講述了靜態文件的擴展需求,這部分在Core的改造中是需要做兼容的,整個的業務邏輯是不變的,但是Core中沒有HttpHanler的處理機制,所有的請求都是通過Middleware來進行處理,Core中對應靜態文件的處理全部是放在StaticFileMiddleware 中,通過IApplicationBuilder.UseStaticFiles進行引用。

? ? 在Core中提供的默認靜態文件加載主要是如下幾個類型:

  • FileExtensionContentTypeProvider:用來確定哪些請求是需要靜態文件StaticFileMiddleware 進行處理的。

  • PhysicalFileInfo:繼承自IFileInfo,用來讀取物理文件內容

  • PhysicalFileProvider:繼承自IFileProvider,用來映射物理路徑到FileInfo和監聽文件是否修改。

? ? Core靜態文件的處理流程如下:

  • 首先通過FileExtensionContentTypeProvider確定是否需要處理請求。

  • 通過PhysicalFileProvider的GetFileInfo返回IFileInfo對象(實際類型是PhysicalFileInfo,如果文件不存在返回NotFoundFileInfo)。

  • 通過IFileInfo的Exists判斷文件是否存在,如果存在通過CreateReadStream方法返回文件內容。

  • 第一次返回給客戶端的ETag和Last-Modified的值,會在第二次請求返回給服務端,如果文件沒有更改則這兩個值在生成規則一樣的情況下不會改變,這時候返回給客戶端304。

  • 三、.Net Core中靜態文件改造

    ? ? 在講述了ERP的靜態文件擴展需求和.Net Core中靜態文件加載原理之后,接下來就是在.Net Core提供的靜態文件功能基礎上進行擴展來接入ERP的擴展需求了,這里我們采用了兩個適配器模式來講解決這個問題,首先我們看看整體的類圖:

    說明:

    • PhysicalFileProviderAdapter 和PhysicalFileProvider 是組合關系,通過重寫GetFileInfo來返回IFileHandler對象,代碼如下:

      public?IFileInfo?GetFileInfo(string?subpath){var?handlers?=?IocManager.ServiceProvider.GetServices<IFileHandler>();var?handler?=?handlers.OrderBy(item?=>?item.Order)//?優先處理特殊的,默認的優先級最小.ToList().First(item?=>{item.InitFile(_provider,?_provider.GetFileInfo(subpath));return?item.CanHandler();});return?handler; }

    • 為了兼容默認靜態文件處理行為,默認的DefaultHandler是可以處理所有的需要處理的請求,所以DefaultHandler的Order最大,SeaConfigJsFileHandler因為需要在JsLangeHandler之前處理所以Order最小。

    • BaseHandler 主要處理通用邏輯,并且可以做一些子類的公用代碼封裝,例如通過如下代碼,確定那種文件類型是這個類需要處理的,這樣在JSLangHandler和CssLangeHandler中就可以少些一些代碼。

      // BaseHandler protected BaseFileHandler(IHttpContextAccessor accessor) {_accessor = accessor;ContentTypeProvider = new FileExtensionContentTypeProvider(); }protected virtual string HandlerContentType { get; } public virtual bool CanHandler() {//通過頭和物理路徑兩種條件來獲取ContentType,如果都獲取不到則不處理if (!ContentTypeProvider.TryGetContentType(_accessor.HttpContext.Request.Path, out var contentType)||!ContentTypeProvider.TryGetContentType(FileInfo.PhysicalPath, out contentType))return false;if (contentType != HandlerContentType)return false;return true; }//CssMergeFileHandler protected override string HandlerContentType => "text/css";public override bool CanHandler() {//忽略swagger的文件return base.CanHandler()&& FileInfo.Name != "swagger-ui.css"; }//JsLangFileHandler protected override string HandlerContentType => "application/javascript";public override bool CanHandler() {//忽略swagger的文件return base.CanHandler()&& FileInfo.Name != "sea-config.js"&& !(FileInfo.Name == "swagger-ui-standalone-preset.js"|| FileInfo.Name == "swagger-ui-bundle.js"); }
    • JsLangFileHandler,CssMergeFileHandler和SeaConfigJsFileHandler都是處理后的內容寫入MemoryStream然后通過CreateReadStream 返回給Core的靜態文件處理。

    • 在BaseHandler中提供InitFile的模板方法,子類重寫InnerInit來實現業務邏輯,AppendHeader用來處理瀏覽器緩存的邏輯。

    //模板方法,所有的初始化邏輯都在此處 public virtual void InitFile([NotNull] IFileProvider provider,[NotNull] IFileInfo fileInfo) {FileProvider=provider?? throw new ArgumentNullException(nameof(provider));FileInfo = fileInfo ?? throw new ArgumentNullException(nameof(fileInfo));if (!CanHandler())return;InnerInit();AppendHeader(); }//留給子類來出來各自的業務邏輯 protected virtual void InnerInit() { } //給返回的靜態文件添加緩存 protected virtual void AppendHeader() {string language = LanguageResourceManager.GetCurrentTopCulture();_accessor.HttpContext.Response.Headers.Append("X-Language", language);string fileVer = _accessor.HttpContext.Request.GetQueryValue("_t");if (string.IsNullOrEmpty(fileVer) ){//沒有版本管理的文件默認添加緩存時間為30*60秒var staticFileExpiresMinutes = GetStaticFileExpires();_accessor.HttpContext.Response.Headers.Append("Cache-Control", $"public,max-age={staticFileExpiresMinutes*60}");_accessor.HttpContext.Response.Headers.Append("X-StaticFileHandler", $"{staticFileExpiresMinutes}minute");}else{//如果有版本管理默認添加一年,因為版本會隨著文件更改而更改_accessor.HttpContext.Response.Headers.Append("Cache-Control", $"public,max-age={TimeSpan.FromDays(365).Seconds}");_accessor.HttpContext.Response.Headers.Append("X-StaticFileHandler",?$"1year");????} }

    四、總結

    ? ? 相比于Framework散落的靜態文件處理,.NET Core的靜態文件處理職責更加明確,點更加集中。基于適配器的擴展之后,將職責更加明確,每個FileHandler只有一個職責,并且在以后需要類似的靜態文件功能時候增加一個FileHandler即可,更加易于擴展。

    ? ? 由于依賴于.Net Core 中Ioc容器提供的獲取實現列表的功能,IEnumerable<T> GetServices<T>(this IServiceProvider provider) 方法,所以這里簡單的采用遍歷判斷的方法,如果只有獲取單個實現的方法的話,這里可以調整為責任鏈模式,有興趣的可以嘗試一下。

    ------ END ------

    作者簡介

    熊同學:?研發工程師,目前負責ERP運行平臺的設計與開發工作。

    也許您還想看

    【復雜系統遷移 .NET Core平臺系列】之遷移項目工程

    【復雜系統遷移 .NET Core平臺系列】之界面層

    招商城科走進武漢研發中心,現場編碼解鎖平臺內核技術

    如何解決大批量數據保存的性能問題

    【2019總結篇】談談數字化時代,ERP如何坐穩數字化底座

    總結

    以上是生活随笔為你收集整理的【复杂系统迁移 .NET Core平台系列】之静态文件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。