使用aspnetcore前後端分離開發,你一定要知道這個

前言

用過Vue單頁面應用開發的,一定都知道Vue-router這個路由組件,它支援hashhistory兩種模式。

HTML5 History 模式

vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新載入。

如果不想要很醜的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新載入頁面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

當你使用 history 模式時,URL 就像正常的 url,例如 //yoursite.com/user/id,也好看!
不過這種模式要玩好,還需要後台配置支援。因為我們的應用是個單頁客戶端應用,如果後台沒有正確的配置,當用戶在瀏覽器直接訪問 //oursite.com/user/id 就會返回 404,這就不好看了。

所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。

aspnetcore使用Vuerouter history模式如何生產部署

今天我們的目的就是如何使用history模式,讓url地址更加簡潔美觀,為了更完整的演示,從頭手把手演示一遍。

創建vue項目

首先安裝nodejs,然後執行下面的npm命令創建vue3項目,跟著提示選擇是或否即可完成項目的創建。本次創建的項目名稱為vue-project

npm init vue@3

image.png

創建aspnetcore的webapi項目

如下圖選擇ASP.Net Core WebApi項目,項目名稱為TestHistory,目錄選擇和上面Vue項目同屬一個文件夾下。
image.png

配置History模式

配置前端

vscode打開前端項目,找到router配置
image.png
由於這裡創建的是Vue3項目模板,模板自動配置好的vuerouter4,
其實下面這種是一樣的。詳情參考,

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

如果要使用hash模式,則使用createWebHashHistory函數創建。

配置後端

如果我們的前端和後端用的是同一個域名,也就是部署在同一個目錄下,則應該將前端編譯後的Html頁面使用aspnetcore的靜態資源進行託管,而不是直接放到根目錄下。
後端首先要添加app.UseStaticFiles();以支援靜態資源託管,然後創建該中間件默認的靜態資源文件目錄wwwroot

部署

將webapi項目發布到本地
image.png
vue項目執行npm run build編譯前端程式碼,將dist目錄下的文件拷貝到到webapi發布後的wwwroot目錄下,

image.png

IIS新建一個站點,這裡使用8080
image.png
別忘記安裝Hosting Bundle

image.png

如果一切順利,打開//localhost:8080你應該會看到這個頁面。

image.png
點擊about還會顯示下面這個頁面,而且地址是//localhost:8080/about,這不就是history模式的效果嗎!
image.png
什麼都沒做,效果就達到了?

別急的得意,在//localhost:8080/about這個地址下,刷新下網頁試試。
image.png
卧槽,404了。

先解釋下為什麼會這樣,當你訪問//localhost:8080時由於iis默認是設置了默認文檔

image.png

image.png
當找不到你請求的資源時,它會嘗試檢查目錄下的默認文檔是否存在,按先後順序檢查,發現存在index.html所以就返回瀏覽器了,所以能夠正常顯示;當你點擊about時,其實只是觸發了頁面的一個事件,頁面有變化,url也變化了,但瀏覽器壓根刷新。當你手動刷新//localhost:8080/about時,就向後端發起這個地址的Get請求,很明顯,我們沒有寫任何Controller來匹配這個路由,wwwroot目錄下也不存在about/index.html當然返回404了。

如何配置history模式,而不導致404

Vuerouter官方文檔給出了部分後端伺服器的配置方式 後端配置例子
這裡只展示aspnetcore常用的伺服器

nginx

location / {
  try_files $uri $uri/ /index.html;
}

Internet Information Services (IIS)

  1. 安裝 IIS UrlRewrite(opens new window)
  2. 在你的網站根目錄中創建一個 web.config 文件,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

nginx的由於沒有環境,沒有測試,應該沒問題,配置也比較簡單。
iis的根據文檔做一遍,UrlRewrite可以理解為是一個中間件,會對請求攔截,對符合規則的url進行路徑重寫,可行。

當然我要做的不是上面的任何一種,因為我們的站點可能會部署到各種各樣的伺服器,每次換伺服器都需要不同的配置來實現,很繁瑣,既然我們aspnetcore擁有強大的中間件系統,為什麼不讓aspnetcore來做這件事呢,不再依賴不同伺服器的配置方案,實現一次編碼,到處運行,在之前的文章中有介紹過如何處理404 《ASP.NETCore統一處理404錯誤都有哪些方式?》
那我們就在404的處理邏輯里實現其實就好了。

直接上程式碼

app.MapFallback(async (context) =>
{
    var phpath = Path.Join(app.Environment.WebRootPath, context.Request.Path);
    var name = Path.Combine(Path.GetDirectoryName(phpath)!, "index.html");
    if (File.Exists(name))
    {
        context.Response.StatusCode = 200;
        await context.Response.SendFileAsync(name);
    }
});

1.當進入404處理邏輯時,首先拼接訪問路徑
2.檢查訪問的路徑所屬的文件夾下是否存在index.html文件
3.當文件存在,則修改響應碼,返回該文件。
4.不存在,什麼也不幹(這裡其實可以做個友好提示頁面)。
重新發布,測試,不管如何刷新,都能正常顯示了。

源碼

Github上獲取://github.com/SpringHgui/TestHistory