筆記目錄

Skip to content

如何將 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,可以將 RequiredStringLength 等屬性自動轉換為 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 補充了文章架構未處理的問題。