Поделиться через


Работа с локальным содержимым в приложениях WebView2

Помимо загрузки удаленного содержимого, содержимое также может быть загружено локально в WebView2. Существует несколько подходов, которые можно использовать для загрузки локального содержимого в элемент управления WebView2, в том числе:

  • Переход по URL-адресу файла.
  • Переход к строке HTML.
  • Сопоставление имен виртуальных узлов.
  • WebResourceRequested Обработка события.

Эти подходы описаны ниже.

Выбор подхода

Различные способы загрузки локального содержимого в элемент управления WebView2 поддерживают следующие сценарии:

Сценарий Переход к URL-адресу файла Переход к строке HTML Использование сопоставления имен виртуальных узлов С помощью WebResourceRequested
API DOM на основе источника ✔️ ✔️ ✔️
API DOM, для которых требуется безопасный контекст ✔️ ✔️
Динамическое содержимое ✔️ ✔️
Дополнительные веб-ресурсы ✔️ ✔️ ✔️
Дополнительные веб-ресурсы, разрешенные в процессе WebView2 ✔️ ✔️

Эти сценарии более подробно описаны ниже.

Загрузка локального содержимого путем перехода по URL-адресу файла

WebView2 позволяет переходить к URL-адресам файлов, загружать базовый HTML-код или PDF-файл. Это самый простой и эффективный подход к загрузке локального содержимого. Однако он менее гибкий, чем другие подходы. Как и в веб-браузере, URL-адреса файлов ограничены некоторыми возможностями:

  • Документ имеет источник, уникальный по пути к файлу. Это означает, что веб-API, для которых требуется источник, например localStorage или indexedDB , будут работать, но сохраненные данные будут недоступны для других локальных документов, загруженных из других путей к файлам.

  • Некоторые веб-API ограничены только защищенными URL-адресами HTTPS и недоступны для документов, загруженных URL-адресами файлов. Сюда входят API- интерфейсы, например navigator.mediaDevices.getUserMedia() для получения видео или звука, navigator.geolocation.getCurrentPosition() доступа к расположению устройства или Notification.requestPermission() запроса разрешения пользователя на отображение уведомлений.

  • Для каждого ресурса необходимо указать полный путь.

  • Чтобы разрешить ссылки на другие локальные файлы из URI файлов или отобразить XML-файлы с примененными преобразованиями XSL, можно задать --allow-file-access-from-files аргумент браузера. См . раздел CoreWebView2EnvironmentOptions.AdditionalBrowserArguments Property.

Рекомендации по загрузке локального содержимого путем перехода по URL-адресу файла

URL-адреса файлов ведут себя так же, как в браузере. Например, невозможно создать XMLHttpRequest (XHR) в URL-адресе файла, так как вы не работаете в контексте веб-страницы.

Необходимо указать полный путь к файлу для каждого ресурса. Например:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
Ресурсы между источниками

При указании URL-адреса файла приложение переходит к файлу на диске, а не к домену в сети. В результате невозможно использовать ресурсы разных источников в итоговом документе.

API DOM на основе источника

Документ, загруженный через URL-адрес файла, имеет источник, уникальный по пути к файлу, как в браузере. Веб-API, для которых требуется источник, localStorage например или indexedDB , будут работать. Однако разные документы, загруженные из разных URL-адресов файлов, считаются не из одного источника и не будут иметь доступа к одним и тем же сохраненным данным.

API DOM, для которых требуется безопасный контекст

Некоторые веб-API ограничены только защищенными URL-адресами HTTPS и недоступны для документов, загруженных URL-адресами файлов. Сюда входят API- интерфейсы, например navigator.mediaDevices.getUserMedia() для получения видео или звука, navigator.geolocation.getCurrentPosition() доступа к расположению устройства или Notification.requestPermission() запроса разрешения пользователя на отображение уведомлений. Дополнительные сведения см. в разделе Безопасные контексты в MDN.

Динамическое содержимое

При загрузке документа через URL-адрес файла содержимое документа поступает из статических файлов на диске. Это означает, что невозможно динамически изменять это локальное содержимое. Это отличается от загрузки документов с веб-сервера, где каждый ответ может создаваться динамически.

Дополнительные веб-ресурсы

Относительное разрешение URL-адресов также работает для документов, загруженных через URL-адрес файла. Это означает, что загруженный документ может содержать ссылки на дополнительные веб-ресурсы, такие как CSS, файлы скриптов или изображений, которые также обслуживаются через URL-адреса файлов.

Дополнительные веб-ресурсы, разрешенные в процессе WebView2

URL-адреса файлов разрешаются в процессах WebView2. Это более быстрый вариант, чем WebResourceRequested, который разрешается в потоке пользовательского интерфейса процесса ведущего приложения.

API для загрузки локального содержимого путем перехода по URL-адресу файла

Пример URL-адреса файла

В этом разделе показано, как выглядит URL-адрес файла для локального пути к файлу содержимого независимо от платформы.

Приложение WebView2 должно кодировать URL-адреса локальных файлов, используя file:/// префикс и косую черту. Например, для примера Demo To Do путь будет следующим:

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

Чтобы скопировать полный путь с префиксом file для локального файла, выполните следующие действия:

  1. При необходимости клонируйте репозиторий Demos, чтобы у вас была локальная копия. См. раздел Шаг 5. Клонирование репозитория демонстраций в статье Установка расширения DevTools для Visual Studio Code.

  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

API DOM на основе источника

Документ, загруженный NavigateToString с помощью метода , имеет расположение , about:blank а его источник — .null Это означает, что веб-API, зависящие от определяемого источника, например localStorage или indexedDB, нельзя использовать.

API DOM, для которых требуется безопасный контекст

Некоторые веб-API ограничены только защищенными URL-адресами HTTPS и недоступны для документов, загруженных с помощью NavigateToString метода , так как их расположение имеет значение about:blank. Сюда входят API- интерфейсы, например navigator.mediaDevices.getUserMedia() для получения видео или звука, navigator.geolocation.getCurrentPosition() доступа к расположению устройства или Notification.requestPermission() запроса разрешения пользователя на отображение уведомлений. Дополнительные сведения см. в разделе Безопасные контексты в MDN.

Динамическое содержимое

При загрузке локального NavigateToString содержимого с помощью метода вы напрямую предоставляете содержимое в качестве параметра для метода . Это означает, что вы управляете содержимым во время выполнения и при необходимости можете динамически создавать его.

Дополнительные веб-ресурсы

Загрузка локального содержимого с помощью NavigateToString метода не позволяет полученному документу ссылаться на дополнительные веб-ресурсы, такие как CSS, изображения или файлы скриптов. Метод позволяет указать только строковое содержимое HTML-документа. Чтобы сослаться на дополнительные веб-ресурсы из HTML-документа, используйте один из других подходов, описанных в этой статье, или представить эти дополнительные веб-ресурсы, встроенные в HTML-документ.

Дополнительные веб-ресурсы, разрешенные в процессе WebView2

NavigateToString не поддерживает дополнительные веб-ресурсы, как упоминалось выше.

API для загрузки локального содержимого путем перехода к строке HTML

Пример строкового представления веб-страницы

Ниже приведено строковое представление веб-страницы Demo To Do . В приведенном ниже списке добавлена оболочка строк для удобства чтения. На практике эти строки объединяются в одну длинную линию:

`<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. Щелкните веб-страницу правой кнопкой мыши и выберите Пункт Проверить , чтобы открыть средства разработки.

  3. В консоли средств разработки введите : document.body.parentElement.outerHTML. Консоль выводит строковое представление веб-страницы:

    Строковое представление веб-страницы Demo To Do

Пример перехода к строке HTML

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

Загрузка локального содержимого с помощью сопоставления имен виртуальных узлов

Другой способ загрузки локального содержимого в элементе управления WebView2 — использовать сопоставление имен виртуальных узлов. Это включает сопоставление имени локального домена с локальной папкой, чтобы при попытке элемента управления WebView2 загрузить ресурс для этого домена он будет загружать содержимое из указанного расположения локальной папки. Источником документа также будет имя виртуального узла.

Такой подход позволяет указать доступ между источниками с помощью перечисления CoreWebView2HostResourceAccessKind .

Из-за текущего ограничения файлы мультимедиа, к которым обращаются с помощью имени виртуального узла, могут загружаться медленно.

Рекомендации по загрузке локального содержимого с помощью сопоставления имен виртуальных узлов

API DOM на основе источника

Локальное содержимое, загруженное с помощью сопоставления имен виртуальных узлов, приводит к порождению документа с URL-адресом HTTP или HTTPS и соответствующим источником. Это означает, что веб-API, для которых требуется источник, например localStorage или indexedDB , будут работать, а другие документы, принадлежащие тому же источнику, смогут использовать сохраненные данные. Дополнительные сведения см. в разделе Политика одно и того же источника в MDN.

API DOM, для которых требуется безопасный контекст

Некоторые веб-API ограничены только защищенными URL-адресами HTTPS. Использование сопоставления имен виртуального узла предоставляет URL-адрес HTTPS для локального содержимого. Это означает, что доступны ТАКИЕ API, как navigator.mediaDevices.getUserMedia() получение видео или звука, navigator.geolocation.getCurrentPosition() доступ к расположению устройства или Notification.requestPermission() запрос разрешения пользователя на отображение уведомлений. Дополнительные сведения см. в разделе Безопасные контексты в MDN.

Динамическое содержимое

При загрузке локального содержимого через сопоставление имени виртуального узла имя виртуального узла сопоставляется с локальной папкой, содержащей статические файлы на диске. Это означает, что невозможно динамически изменять это локальное содержимое. Это отличается от загрузки документов с веб-сервера, где каждый ответ может создаваться динамически.

Дополнительные веб-ресурсы

Локальное содержимое, загруженное через сопоставление имен виртуальных узлов, имеет URL-адрес HTTP или HTTPS, поддерживающий относительное разрешение URL-адресов. Это означает, что загруженный документ может содержать ссылки на дополнительные веб-ресурсы, такие как CSS, файлы скриптов или изображений, которые также обслуживаются с помощью сопоставления имен виртуальных узлов.

Дополнительные веб-ресурсы, разрешенные в процессе WebView2

URL-адреса имен виртуальных узлов разрешаются в процессах WebView2. Это более быстрый вариант, чем WebResourceRequested, который разрешается в потоке пользовательского интерфейса процесса ведущего приложения.

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 событие, см . раздел Регистрация пользовательской схемы в статье Обзор функций и API WebView2.

Рекомендации по загрузке локального содержимого путем обработки события WebResourceRequested

API DOM на основе источника

Локальное содержимое загружается с помощью WebResourceRequested результатов в документе с URL-адресом HTTP или HTTPS и соответствующим источником. Это означает, что веб-API, для которых требуется источник, например localStorage или indexedDB , будут работать, а другие документы, принадлежащие тому же источнику, смогут использовать сохраненные данные. Дополнительные сведения см. в разделе Политика одно и того же источника в MDN.

API DOM, для которых требуется безопасный контекст

Некоторые веб-API ограничены только защищенными URL-адресами HTTPS. Использование WebResourceRequested позволяет заменить запросы веб-ресурсов URL-адреса HTTPS собственным локальным содержимым. Это означает, что доступны ТАКИЕ API, как navigator.mediaDevices.getUserMedia() получение видео или звука, navigator.geolocation.getCurrentPosition() доступ к расположению устройства или Notification.requestPermission() запрос разрешения пользователя на отображение уведомлений. Дополнительные сведения см. в разделе Безопасные контексты в MDN.

Динамическое содержимое

При загрузке локального содержимого с помощью WebResourceRequestedукажите локальное содержимое для загрузки в обработчике событий. Это означает, что вы управляете содержимым во время выполнения и при необходимости можете динамически создавать его.

Дополнительные веб-ресурсы

WebResourceRequested изменяет содержимое, загружаемое через URL-адреса HTTP или HTTPS, которые поддерживают относительное разрешение URL-адресов. Это означает, что результирующий документ может содержать ссылки на дополнительные веб-ресурсы, такие как CSS, файлы скриптов или изображений, которые также обслуживаются через WebResourceRequested.

Дополнительные веб-ресурсы, разрешенные в процессе WebView2

При загрузке содержимого через URL-адрес файла или сопоставление имени виртуального узла разрешение происходит в процессах WebView2. WebResourceRequested Однако событие возникает в потоке пользовательского интерфейса WebView2 процесса ведущего приложения, что может привести к замедлению загрузки результирующего документа.

  1. WebView2 сначала приостанавливает загрузку веб-страницы, чтобы дождаться отправки события в процесс ведущего приложения.
  2. Затем WebView2 ожидает доступности потока пользовательского интерфейса.
  3. Затем WebView2 ожидает, пока код приложения обработает событие.

Это может занять некоторое время. Обязательно ограничьте вызовы AddWebResourceRequestedFilter только веб-ресурсами, которые должны вызывать WebResourceRequested событие.

API для загрузки локального содержимого путем обработки события WebResourceRequested

Пример обработки события 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", "");
    }
};

См. также