如何將 Vue 3 與 ASP.NET Razor 一起使用
TLDR
- 建議在輕量級或非建構工具(Build Tools)環境下,使用 Vue 3 的 Options API 進行開發。
- 透過自定義
TagHelper,可將 ASP.NET Core 的DataAnnotations自動轉換為vee-validate的驗證規則。 - 整合時需注意
v-model與原生value屬性的衝突,建議透過TagHelper自動移除value屬性。 - 透過
axios攔截器(Interceptor)自動注入RequestVerificationToken,以確保ValidateAntiForgeryToken驗證機制正常運作。 - 警告:此架構在後續測試中發現
asp-page-handler無法正常運作,且無法正確解析DisplayName,建議評估改用 Blazor 或其他更穩定的整合方案。
整合架構與核心實作
在 ASP.NET Razor Pages 中整合 Vue 3,主要目標是利用前端套件(如 vee-validate)進行驗證,同時保留 Razor 的伺服器端渲染能力。
處理 Vue 與 Razor 的屬性衝突
什麼情況下會遇到這個問題:當在 Razor 頁面中使用 asp-for 產生 input 標籤,同時又加上 v-model 時,原生 value 屬性會導致 Vue 的資料綁定出現警告或異常。
解決方式是建立一個 VueInputTagHelper,在檢測到 v-model 存在時,自動移除 value 屬性:
csharp
[HtmlTargetElement("input", Attributes = ForAttributeName, TagStructure = TagStructure.WithoutEndTag)]
public class VueInputTagHelper : TagHelper {
private const string ForAttributeName = "asp-for";
private const string VueModelAttributeName = "v-model";
public override void Process(TagHelperContext context, TagHelperOutput output) {
string[] excludeTypes = new string[] { "radio", "checkbox" };
if (context.AllAttributes.ContainsName(VueModelAttributeName) && !excludeTypes.Contains(context.AllAttributes["type"].Value)) {
output.Attributes.RemoveAt(output.Attributes.IndexOfName("value"));
}
}
}自動化前端驗證規則
什麼情況下會遇到這個問題:手動維護前端驗證規則與後端 DataAnnotations 不一致時。
透過自定義 VeeValidateInputTagHelper,可以將 Required、StringLength 等屬性自動轉換為 vee-validate 可識別的 rules 字串:
csharp
private string? GetRules() {
List<string> items = new List<string>();
// 遍歷 Metadata 中的驗證屬性並轉換
foreach (var validationAttribute in For.Metadata.ValidatorMetadata) {
switch (validationAttribute) {
case RequiredAttribute _:
items.Add("required");
break;
case EmailAddressAttribute _:
items.Add("email");
break;
// 其他屬性轉換邏輯...
}
}
return items.Any() ? $"{string.Join("|", items)}" : null;
}安全性與 Ajax 請求
什麼情況下會遇到這個問題:使用 axios 發送 POST 請求時,若未帶入 RequestVerificationToken,會導致 ASP.NET Core 的 ValidateAntiForgeryToken 驗證失敗。
在 site.js 中設定 axios 攔截器,確保每次請求都會帶上 Token:
javascript
axios.interceptors.request.use(
config => {
let token = document.querySelector('input[name="__RequestVerificationToken"]');
if (token !== null) {
config.headers = {
RequestVerificationToken: token.value
}
}
return config;
}
);踩雷紀錄與架構評估
WARNING
此架構在後續維護中發現以下嚴重限制:
VeeValidateFormTagHelper產生的<v-form>會導致asp-page-handler無法正常運作。- 錯誤訊息無法正確顯示
DisplayName等 Attribute 所設定的欄位名稱。 - 由於前端框架版本更新頻繁,此類「混搭」架構容易產生不相容問題。
基於上述原因,建議開發者在選擇技術棧時:
- 若專案規模較大,建議直接採用 ASP.NET Core Blazor,避免前端與後端驗證邏輯脫鉤。
- 若必須使用 Vue 3,應考慮將前端完全獨立為 SPA 專案,透過 API 進行溝通,而非強行與 Razor Pages 進行 DOM 層級的整合。
異動歷程
- 2023-01-30 初版文件建立。
- 2024-04-07 補充了文章架構未處理的問題。
