共用方式為


在 WebView2 應用程式中使用本機內容

除了載入遠端內容之外,內容也可以在本機載入 WebView2。 有數種方法可用來將本機內容載入 WebView2 控制件,包括:

  • 流覽至檔案URL。
  • 流覽至 HTML 字串。
  • 虛擬主機名對應。
  • WebResourceRequested處理事件。

這些方法如下所述。

選取方法

將本機內容載入 WebView2 控制件的各種方式支援下列案例:

案例 流覽至檔案URL 流覽至 HTML 字串 使用虛擬主機名對應 使用 WebResourceRequested
以原始來源為基礎的 DOM API ✔️ ✔️ ✔️
需要安全內容的 DOM API ✔️ ✔️
動態內容 ✔️ ✔️
其他 Web 資源 ✔️ ✔️ ✔️
在 WebView2 程式中解析的其他 Web 資源 ✔️ ✔️

以下將詳細說明這些案例。

流覽至檔案 URL 以載入本機內容

WebView2 允許導覽至檔案 URL,以載入基本 HTML 或 PDF。 這是載入本機內容最簡單且最有效率的方法。 不過,相較於其他方法,其彈性較低。 如同在網頁瀏覽器中,檔案 URL 在某些功能中受到限制:

  • 檔的來源是其檔案路徑的唯一來源。 這表示需要 原點的 Web API,例如 localStorageindexedDB 將會運作,但從其他檔案路徑載入的其他本機檔將無法使用儲存的數據。

  • 某些 Web API 僅限於安全的 HTTPS URL,不適用於檔案 URL 所載入的檔。 這包括 API,例如 navigator.mediaDevices.getUserMedia() 取得視訊或音效、 navigator.geolocation.getCurrentPosition() 存取裝置的位置,或 Notification.requestPermission() 要求使用者顯示通知的許可權。

  • 針對每個資源,必須指定完整路徑。

  • 若要允許從檔案 URI 參考其他本機檔案,或顯示已套用 XSL 轉換的 XML 檔案,您可以設定 --allow-file-access-from-files 瀏覽器自變數。 請參閱 CoreWebView2EnvironmentOptions.AdditionalBrowserArguments 屬性

流覽至檔案 URL 載入本機內容的考慮

檔案 URL 的行為與它們在瀏覽器中的行為一樣。 例如,您無法在檔案 URL 中建立 XMLHttpRequest (XHR) ,因為您無法在網頁的內容中工作。

您必須為每個資源指定檔案的完整路徑。 例如:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
跨原始來源資源

指定檔案 URL 時,應用程式會流覽至磁碟上的檔案,而不是網路上的網域。 因此,無法在產生的檔中使用跨原始來源資源。

以原始來源為基礎的 DOM API

透過檔案 URL 載入的檔具有其檔案路徑唯一的來源,就像在瀏覽器中一樣。 需要原點的 Web API,例如 localStorageindexedDB 將會運作。 不過,從不同檔案 URL 載入的不同檔不會被視為來自相同的來源,而且將無法存取相同的儲存數據。

需要安全內容的 DOM API

某些 Web API 僅限於安全的 HTTPS URL,不適用於檔案 URL 所載入的檔。 這包括 API,例如 navigator.mediaDevices.getUserMedia() 取得視訊或音效、 navigator.geolocation.getCurrentPosition() 存取裝置的位置,或 Notification.requestPermission() 要求使用者顯示通知的許可權。 如需詳細資訊,請參閱 MDN 上 的安全內容

動態內容

透過檔案 URL 載入檔時,檔的內容來自磁碟上的靜態檔案。 這表示無法動態修改此本機內容。 這不同於從 Web 伺服器載入檔,其中可以動態產生每個回應。

其他 Web 資源

相對 URL 解析也適用於透過檔案 URL 載入的檔。 這表示載入的檔可以參考其他 Web 資源,例如 CSS、腳本或圖像檔,這些資源也會透過檔案 URL 提供。

在 WebView2 程式中解析的其他 Web 資源

檔案 URL 會在 WebView2 進程中解析。 這是比 更快 WebResourceRequested的選項,後者會在主應用程式進程UI線程中解析。

流覽至檔案 URL 以載入本機內容的 API

檔案 URL 的範例

本節會以與平台無關的方式,顯示本機內容檔案路徑的檔案 URL 外觀。

WebView2 應用程式需要使用 file:/// 前置詞和正斜線來撰寫本機檔案 URL 的程序代碼。 例如,針對示範待辦範例,路徑會是:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html

若要複製本機檔案具有「檔案」前置詞的完整路徑:

  1. 或者,複製 Demos 存放庫,讓您擁有本機複本。 請參閱安裝適用於 Visual Studio Code 的 DevTools 擴充功能中的步驟 5:複製示範存放庫

  2. 在 Microsoft Edge 中,按 Ctrl+O 開啟檔案。 開啟本機 .html 檔案,例如本機複製的檔案 Demos/demo-to-do/index.html

    C:\Users\username\Documents\GitHub\Demos\demo-to-do\index.html

    網址列一開始不會顯示 file:/// 前置詞,但開頭為驅動器號:

    C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
    

    Microsoft Edge 的網址列一開始會隱藏 file:/// 前置詞

  3. 按兩下網址列,然後按 住家 鍵,或按 Ctrl+A 選取整個路徑。

    Microsoft Edge 的網址列現在會顯示 file:/// 前置詞

    包含 的整個檔案路徑 file:/// 會複製到剪貼簿緩衝區中,因此您可以貼上包含前置詞的 file:/// 完整路徑:

    file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
    

另請參閱:

流覽至檔案 URL 的範例

webView.CoreWebView2.Navigate(
          "file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html");

流覽至 HTML 字串以載入本機內容

載入本機內容的另一個方法是 NavigateToString 方法。 這種方法會直接從字串將內容載入 WebView2。 如果您要透過應用程式程式代碼封裝內容,或想要動態建立內容,這會很有用。

如果您想要載入無法透過 URL 存取的內容,則流覽至字串的另一個案例可能很有用。 例如,如果您有 HTML 檔的記憶體內部表示法,您可以使用 NavigateToString 方法將該內容載入 WebView2 控制件。 如果您想要避免在將內容載入控制項之前將內容寫入至檔案或伺服器,這會很有用。

流覽至 HTML 字串載入本機內容的考慮

傳遞至 NavigateToString 方法的 HTML 內容字串大小限制為 2MB。 當字串包含內嵌的額外資源時,此大小限制可能很容易超過。 如果超過此大小限制,則會傳回錯誤:「值不在預期的範圍內」。

以原始來源為基礎的 DOM API

使用 NavigateToString 方法載入的檔案,其位置設定為 about:blank ,且原始來源設定為 null。 這表示無法使用相依於所定義來源的 Web API,例如 localStorageindexedDB

需要安全內容的 DOM API

某些 Web API 僅限於保護 HTTPS URL,無法透過 方法載入的檔案使用, NavigateToString 因為其位置設定為 about:blank。 這包括 API,例如 navigator.mediaDevices.getUserMedia() 取得視訊或音效、 navigator.geolocation.getCurrentPosition() 存取裝置的位置,或 Notification.requestPermission() 要求使用者顯示通知的許可權。 如需詳細資訊,請參閱 MDN 上 的安全內容

動態內容

透過 NavigateToString 方法載入本機內容時,您會直接提供內容作為方法的參數。 這表示您在運行時間控制內容,而且您可以視需要動態產生內容。

其他 Web 資源

使用 NavigateToString 方法載入本機內容,無法讓產生的文件參考其他 Web 資源,例如 CSS、影像或腳本檔案。 方法只可讓您指定 HTML 檔案的字串內容。 若要參考 HTML 檔中的其他 Web 資源,請使用本文所述的其他方法之一,或在 HTML 檔中以內嵌方式表示這些額外的 Web 資源。

在 WebView2 程式中解析的其他 Web 資源

NavigateToString 不支援其他 Web 資源,如上所述。

流覽至 HTML 字串以載入本機內容的 API

網頁的字串表示範例

以下是示範 待辦 網頁的字串表示。 下列清單已新增換行以方便閱讀。 實際上,這些行會串連成單一長行:

`<html lang="en"><head>\n    
<meta charset="UTF-8">\n    
<meta name="viewport" content="width=device-width, initial-scale=1.0">\n    
<title>TODO app</title>\n    
<link rel="stylesheet" href="styles/light-theme.css" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)">\n    
<link rel="stylesheet" href="styles/dark-theme.css" media="(prefers-color-scheme: dark)">\n    
<link rel="stylesheet" href="styles/base.css">\n    
<link rel="stylesheet" href="styles/to-do-styles.css">\n    
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📋
</text></svg>">\n  
</head>\n\n  
<body>\n    
<h1>📋 My tasks</h1>\n    
<form>\n      
<div class="new-task-form" tabindex="0">\n        
<label for="new-task">➕ Add a task</label>\n        
<input id="new-task" autocomplete="off" type="text" placeholder="Try typing 'Buy milk'" title="Click to start adding a task">\n        
<input type="submit" value="➡️">\n      
</div>\n      
<ul id="tasks"><li class="divider">No tasks defined</li></ul>\n    
</form>\n\n    \x3Cscript src="to-do.js">\x3C/script>\n  \n\n
</body>
</html>`

若要取得上述字串:

  1. 移至 示範執行

  2. 以滑鼠右鍵按兩下網頁,然後選取 [ 檢查 ] 以開啟DevTools。

  3. 在DevTools的 控制台 中,輸入: document.body.parentElement.outerHTML主控台會輸出網頁的字串表示:

    示範待辦網頁的字串表示

巡覽至 HTML 字串的範例

// Define htmlString with the string representation of HTML as above.
webView.CoreWebView2.NavigateToString(htmlString);

使用虛擬主機名對應載入本機內容

在 WebView2 控制件中載入本機內容的另一種方式是使用虛擬主機名對應。 這牽涉到將本機功能變數名稱對應至本機資料夾,因此當 WebView2 控制件嘗試載入該網域的資源時,它會改為從指定的本機資料夾位置載入內容。 檔的來源也會是虛擬主機名。

此方法可讓您使用 CoreWebView2HostResourceAccessKind 列舉來指定跨原始來源存取。

由於目前的限制,使用虛擬主機名存取的媒體檔案載入速度可能會變慢。

使用虛擬主機名對應載入本機內容的考慮

以原始來源為基礎的 DOM API

透過虛擬主機名對應載入的本機內容會產生具有 HTTP 或 HTTPS URL 以及對應來源的檔。 這表示需要原點的 Web API,例如 localStorageindexedDB 將會運作,而屬於相同來源的其他檔將能夠使用儲存的數據。 如需詳細資訊,請參閱 MDN 上的 相同原始來源 原則。

需要安全內容的 DOM API

某些 Web API 僅限於安全的 HTTPS URL。 使用虛擬主機名對應可為您的本機內容提供 HTTPS URL。 這表示可以使用 API,例如 navigator.mediaDevices.getUserMedia() 取得視訊或音效、 navigator.geolocation.getCurrentPosition() 存取裝置的位置,或 Notification.requestPermission() 要求使用者顯示通知的許可權。 如需詳細資訊,請參閱 MDN 上 的安全內容

動態內容

透過虛擬主機名對應載入本機內容時,您會將虛擬主機名對應至磁碟上包含靜態檔案的本機資料夾。 這表示無法動態修改此本機內容。 這不同於從 Web 伺服器載入檔,其中可以動態產生每個回應。

其他 Web 資源

透過虛擬主機名對應載入的本機內容具有支持相對 URL 解析的 HTTP 或 HTTPS URL。 這表示載入的檔可以參考其他 Web 資源,例如 CSS、腳本或圖像檔,這些資源也會透過虛擬主機名對應來提供。

在 WebView2 程式中解析的其他 Web 資源

虛擬主機名 URL 會在 WebView2 進程中解析。 這是比 更快 WebResourceRequested的選項,後者會在主應用程式進程UI線程中解析。

使用虛擬主機名對應載入本機內容的 API

虛擬主機名對應的範例

webView.CoreWebView2.SetVirtualHostNameToFolderMapping("demo", 
         "C:\Github\Demos\demo-to-do", CoreWebView2HostResourceAccessKind.DenyCors);
webView.CoreWebView2.Navigate("https://demo/index.html");

藉由處理 WebResourceRequested 事件載入本機內容

在 WebView2 控制件中裝載本機內容的另一種方式是依賴 WebResourceRequested 事件。 當控件嘗試載入資源時,就會觸發此事件。 您可以使用此事件來攔截要求並提供本機內容,如 自定義網路要求管理中所述。

WebResourceRequested 可讓您根據每個要求自定義本機內容的行為。 這表示您可以決定要攔截哪些要求並提供您自己的內容,以及要讓 WebView2 控制件正常處理的要求。 不過,自定義行為需要更多程序代碼,例如虛擬主機對應,而且需要具備 HTTP 的知識,才能建構適當的回應。

從 WebView2 的觀點來看,資源將會透過網路傳送,而 WebView2 會遵守應用程式在回應中設定的標頭。 WebResourceRequested使用事件的速度也比其他方法慢,因為每個要求都需要跨進程通訊和處理。

自訂配置註冊

如果您想要使用自定義配置來提出產生事件的 WebResourceRequested Web 資源要求,請參閱 WebView2 功能和 API 概觀中的自定義配置註冊

處理 WebResourceRequested 事件載入本機內容的考慮

以原始來源為基礎的 DOM API

透過 WebResourceRequested 載入的本機內容會產生具有 HTTP 或 HTTPS URL 和對應來源的檔。 這表示需要原點的 Web API,例如 localStorageindexedDB 將會運作,而屬於相同來源的其他檔將能夠使用儲存的數據。 如需詳細資訊,請參閱 MDN 上的 相同原始來源 原則。

需要安全內容的 DOM API

某些 Web API 僅限於安全的 HTTPS URL。 使用 WebResourceRequested 可讓您將 HTTPS URL Web 資源要求取代為您自己的本機內容。 這表示可以使用 API,例如 navigator.mediaDevices.getUserMedia() 取得視訊或音效、 navigator.geolocation.getCurrentPosition() 存取裝置的位置,或 Notification.requestPermission() 要求使用者顯示通知的許可權。 如需詳細資訊,請參閱 MDN 上 的安全內容

動態內容

透過 WebResourceRequested載入本機內容時,您可以指定要在事件處理程式中載入的本機內容。 這表示您在運行時間控制內容,而且您可以視需要動態產生內容。

其他 Web 資源

WebResourceRequested 修改透過支持相對 URL 解析的 HTTP 或 HTTPS URL 載入的內容。 這表示產生的檔可以參考其他 Web 資源,例如 CSS、腳本或圖像檔,這些資源也會透過 WebResourceRequested提供。

在 WebView2 程式中解析的其他 Web 資源

透過檔案 URL 或虛擬主機名對應載入內容時,會在 WebView2 進程中進行解析。 不過, WebResourceRequested 事件會在主應用程式程式的 WebView2 UI 線程上引發,這可能會導致所產生檔的載入速度變慢。

  1. WebView2 會先暫停載入網頁,以等候事件傳送至主應用程式程式。
  2. 然後 WebView2 會等候您的 UI 線程可供使用。
  3. WebView2 接著會等候您的應用程式程式代碼處理事件。

這可能需要一些時間。 請務必只對必須引發事件的 WebResourceRequested Web 資源進行呼叫AddWebResourceRequestedFilter

透過處理 WebResourceRequested 事件載入本機內容的 API

處理 WebResourceRequested 事件的範例

// Reading of response content stream happens asynchronously, and WebView2 does not 
// directly dispose the stream once it read.  Therefore, use the following stream
// class, which properly disposes when WebView2 has read all data.  For details, see
// [CoreWebView2 does not close stream content](https://github.com/MicrosoftEdge/WebView2Feedback/issues/2513).
class ManagedStream : Stream {
    public ManagedStream(Stream s)
    {
        s_ = s;
    }

    public override bool CanRead => s_.CanRead;

    public override bool CanSeek => s_.CanSeek;

    public override bool CanWrite => s_.CanWrite;

    public override long Length => s_.Length;

    public override long Position { get => s_.Position; set => s_.Position = value; }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return s_.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int read = 0;
        try
        {
            read = s_.Read(buffer, offset, count);
            if (read == 0)
            {
                s_.Dispose();
            }
        } 
        catch
        {
            s_.Dispose();
            throw;
        }
        return read;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

   private Stream s_;
}
webView.CoreWebView2.AddWebResourceRequestedFilter("https://demo/*", 
                                                CoreWebView2WebResourceContext.All);
webView.CoreWebView2.WebResourceRequested += delegate (object sender, 
                                     CoreWebView2WebResourceRequestedEventArgs args)
{
    string assetsFilePath = "C:\\Demo\\" + 
                            args.Request.Uri.Substring("https://demo/*".Length - 1);
    try
    {
        FileStream fs = File.OpenRead(assetsFilePath);
        ManagedStream ms = new ManagedStream(fs);
        string headers = "";
        if (assetsFilePath.EndsWith(".html"))
        {
            headers = "Content-Type: text/html";
        }
        else if (assetsFilePath.EndsWith(".jpg"))
        {
            headers = "Content-Type: image/jpeg";
        } else if (assetsFilePath.EndsWith(".png"))
        {
            headers = "Content-Type: image/png";
        }
        else if (assetsFilePath.EndsWith(".css"))
        {
            headers = "Content-Type: text/css";
        }
        else if (assetsFilePath.EndsWith(".js"))
        {
            headers = "Content-Type: application/javascript";
        }

        args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse(
                                                            ms, 200, "OK", headers);
    }
    catch (Exception)
    {
        args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse(
                                                        null, 404, "Not found", "");
    }
};

另請參閱