解決方案寫在前面:更新Visual Studio及相關元件,本人版本自17.8.0更新至17.11.4
緣起於公司的一個業務介面,在有一些資訊需要在應用內嵌的webview中展示,資訊不少,涉及的前端技術不復雜,但是拼字串太羅嗦,所以想到了新增一個Razor頁面,所以,常規邏輯,在服務上註冊'''AddRazorPages''',構建後使用對映方法'''MapRazorPages''',然後除錯。這次除錯沒有問題,頁面也能正常顯示。因為是微信的小程式,並且是在手機上演示,網站地址要在配置的業務域名上,所以基本寫完了頁面,第一時間就把應用部署到了iis的測試埠上。然後問題就發生了————配置的首頁返回404,訪問業務所在的url也返回404。
因為幾乎是第一次正式用Razor,所以嘗試了檢查url,補全index甚至補全.cshtml,都是一樣的404。再後來檢查了應用的日誌,發現除了頁面訪問的w3cLog記錄,沒有任何其他的記錄,但頁面使用了Ef core 對SQl Server 的查詢,而查詢的日誌項並沒有在日誌過濾規則之外,所以基本確定了頁面並沒有被執行。
本地除錯可以執行,部署後不能執行,第一時間就是想到的環境不一致,Development和Production,對應開發環境和生產環境。在Visual Studio使用除錯時ASPNETCORE_ENVIRONMENT這個變數要被設定成"Development"的,但直接執行,該環境變數沒有定義,應用預設的狀態是Production。
在Debug資料夾執行可執行檔案,結果發現依然404。注意到Pages 資料夾沒有,第一時間考慮有兩種可能:一、專案設定錯誤,沒有在生成時把檔案複製到資料夾;二、考慮cshtml檔案被編譯到dll中了(實際上就是這樣的,但當時的我並不能確認)。
先考慮第一種可能,這個實操不難,只需要在專案配置中新增
<Content Update="Pages\*.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
再生成一次,第一遍不配置ASPNETCORE_ENVIRONMENT變數,結果是相關頁面依舊404,第二遍,配置ASPNETCORE_ENVIRONMENT為Development,終於,頁面正常展示了。但奇怪的是,進一步釋出->執行時,依然需要將ASPNETCORE_ENVIRONMENT變數設定為Development??這不應該!(此時的我依然沒覺得複製Pages/*.cshtml有什麼問題,就是覺得配置被搞出問題了)設定為開發環境顯然不合理,很多配置都是針對開發環境的,SwaggerUI也會展示,總不能在釋出到伺服器前再改一次程式碼吧?
不過,由於第二天要演示,而且由於微信小程式的各種**門檻,不得不部署,這時候IIS又鬧情緒,照網上的辦法,又是配置
演示結束,立馬回到這個問題,首先想到的就是新建一個專案。這下可不得了了,不論改環境變數還是複製檔案,都成了404了。明明就是預設的Razor Pages模板,去MSDN上給的程式碼也都一樣,但就是跑不起來。無頭蒼蠅一樣搜了各種辦法,但都沒有針對我的問題,尋找文心一言和chatGPT的解答,也都是不合我意。
不過,最終在各種關鍵詞搜尋中,找到了楊中科老師在百家號上的一篇回答《如何讓asp.net core mvc釋出時候不編譯cshtml檢視》,雖然和我的想法相反,但這總算是相關的了。文章中介紹了讓cshtml檔案在執行時編譯的方法,感覺能找到點苗頭,於是按他說的一試,頁面不再是404了。
或許到這,也就該結束了,畢竟用這方法不在考慮修改環境變數,也不用修改其他程式碼,問題算是圓滿的解決了。。。嗎?當然可以不再進一步探索,但是這篇反方向的解決方案讓我有了一個新的靈感————:
文章滿篇都在提要在執行時編譯要做的額外配置,那要是不做呢?預設情況下它到底以何種方式執行。楊中科老師的文章中提到的兩個屬性標籤已經讓答案呼之欲出了:MvcRazorCompileOnPublish被置為了false,RazorCompileOnBuild同樣被置為了false,也就是說,預設情況下,生成時和釋出時Razor檔案可能是被編譯的。至於編譯的形式,有可能就是jsp那樣!(至於為什麼先知道的jsp,學校給教的,大概在2020~2021年,哈哈:),現在估計依然在教。也是完成作業的過程中偶爾接觸到的jsp檔案編譯後的反編譯程式碼)
要探究編譯後的檔案,自然是少不了ILSpy,以往用它來分析過線上程式碼的問題,所以想到反編譯就想到了它。對釋出後的dll(沒有使用執行時編譯配置)進行分析,發現相關的名稱空間下只有Model檔案的編譯,頁面內容相關的程式碼一點沒有。
但現在,在當前的環境下,我也是窮途末路了,新專案都這樣,就算又有配置問題,找出來也不異於大海撈針,況且如此的絕望,在第一天下班後的嘗試中就已經發生在了自己的電腦上。只有用本辦法了————虛擬機器。在一臺Windows10環境的虛擬機器上,我又重新搭建了一套Visual Studio,建立了新的Razor模板,執行。但這次,成功了,用了各種姿勢都是。我不知道該說是意料之中還是意料之外。但總算是有成功的樣本給我研究了。
拿到虛擬機器上釋出後的dll,丟到ILSpy中,這次能清楚的看到被編譯到dll當中的頁面內容,也不出意外,整體形式和jsp差不多。
但為什麼?這一遭讓我知道了cshtml在不特殊設定的情況下確實要被編譯到dll中去的,相應的,把cshtml複製到構建資料夾並無必要。我一開始考慮的配置問題似乎就不存在……
剩下的就是對專案無盡的蹂躪了。當我在一次次的生成、除錯或是釋出時,我注意到一條警告:
CS9057 分析器程式集“C:\Program Files\dotnet\sdk\8.0.201\Sdks\Microsoft.NET.Sdk.Razor\source-generators\Microsoft.CodeAnalysis.Razor.Compiler.SourceGenerators.dll”引用了編譯器的版本“4.9.0.0”,該版本高於當前正在執行的版本“4.8.0.0”
要放在平時,我根本不會在意,畢竟滿屏的“屬性未在構造器中賦值”中,它並不起眼,況且,就算偶然注意到它,也已經在多次偶然中與它相熟了。往常也沒有奇怪的異常與它相關。但這次顯眼Compiler和SourceGenerators讓我無法再忽視它的存在。cshtml不就是在編譯時沒有生成成相應的程式碼嘛。
在前面蹂躪的過程中,也試了使用dotnet publish,結果是成功的,換了不同的sdk版本,都是成功。但自從注意到這條警告,我就試圖在dotnet 的生成和釋出中找到它,始終無果。
VS編譯器版本不相容了?怎麼更新一下?vs編譯用的不是我安裝的sdk?
此時,自然就想到了更新Visual Studio,找到 vs installer 發現確實有新版本,發現版本號差的並不是很大,所以一開始並沒有報太大希望,但更新後再編譯,一切問題都消失了……
屮
困擾我這麼久的問題,竟然一次更新就解決了,完全就沒有幻想中力挽狂瀾的澎湃啊啊啊啊啊~~~