Compartilhar via


Trabalhar com conteúdo local em aplicativos WebView2

Além de carregar conteúdo remoto, o conteúdo também pode ser carregado localmente no WebView2. Há várias abordagens que podem ser usadas para carregar conteúdo local em um controle WebView2, incluindo:

  • Navegando até uma URL de arquivo.
  • Navegando até uma cadeia de caracteres HTML.
  • Mapeamento do nome do host virtual.
  • Manipulando o WebResourceRequested evento.

Essas abordagens são descritas abaixo.

Selecionando uma abordagem

As várias maneiras de carregar conteúdo local em um controle WebView2 dão suporte aos seguintes cenários:

Cenário Navegando até uma URL de arquivo Navegando até uma cadeia de caracteres HTML Usando o mapeamento de nomes de host virtual Usando WebResourceRequested
APIs DOM baseadas em origem ✔️ ✔️ ✔️
APIs DOM que exigem contexto seguro ✔️ ✔️
Conteúdo dinâmico ✔️ ✔️
Recursos web adicionais ✔️ ✔️ ✔️
Recursos web adicionais resolvidos no processo WebView2 ✔️ ✔️

Esses cenários são descritos em mais detalhes abaixo.

Carregando conteúdo local navegando até uma URL de arquivo

O WebView2 permite que as navegaçãos registrem URLs, carreguem HTML básico ou um PDF. Essa é a abordagem mais simples e eficiente para carregar conteúdo local. No entanto, ele é menos flexível do que as outras abordagens. Como em um navegador da Web, as URLs de arquivo são limitadas em alguns recursos:

  • O documento tem uma origem exclusiva para seu caminho de arquivo. Isso significa que as APIs Web que exigem uma origem como localStorage ou indexedDB funcionarão, mas os dados armazenados não estarão disponíveis para outros documentos locais carregados de outros caminhos de arquivo.

  • Algumas APIs Web são limitadas apenas para proteger URLs HTTPS e não estão disponíveis para documentos carregados por URLs de arquivo. Isso inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() acessar a localização do dispositivo ou Notification.requestPermission() solicitar a permissão do usuário para exibir notificações.

  • Para cada recurso, o caminho completo deve ser especificado.

  • Para permitir referências a outros arquivos locais de URIs de arquivo ou exibir arquivos XML com transformações XSL aplicadas, você pode definir o argumento do --allow-file-access-from-files navegador. Consulte Propriedade CoreWebView2EnvironmentOptions.AdditionalBrowserArguments.

Considerações sobre o carregamento de conteúdo local navegando até uma URL de arquivo

As URLs de arquivo se comportam como no navegador. Por exemplo, você não pode fazer um XMLHttpRequest (XHR) em uma URL de arquivo, porque você não está trabalhando no contexto de uma página da Web.

Você deve especificar o caminho completo do arquivo para cada recurso. Por exemplo:

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

Ao especificar uma URL de arquivo, o aplicativo navega até um arquivo em disco, não para um domínio na rede. Como resultado, não é possível usar recursos de origem cruzada no documento resultante.

APIs DOM baseadas em origem

Um documento carregado por meio de uma URL de arquivo tem uma origem exclusiva para seu caminho de arquivo, assim como no navegador. APIs Web que exigem uma origem como localStorage ou indexedDB funcionarão. No entanto, documentos diferentes carregados de URLs de arquivo diferentes não são considerados da mesma origem e não terão acesso aos mesmos dados armazenados.

APIs DOM que exigem contexto seguro

Algumas APIs Web são limitadas apenas para proteger URLs HTTPS e não estão disponíveis para documentos carregados por URLs de arquivo. Isso inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() acessar a localização do dispositivo ou Notification.requestPermission() solicitar a permissão do usuário para exibir notificações. Consulte Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar um documento por meio de uma URL de arquivo, o conteúdo do documento vem de arquivos estáticos em disco. Isso significa que não é possível modificar dinamicamente esse conteúdo local. Isso é diferente de carregar documentos de um servidor Web, em que cada resposta pode ser gerada dinamicamente.

Recursos web adicionais

A resolução de URL relativa também funciona para documentos carregados por meio de uma URL de arquivo. Isso significa que o documento carregado pode ter referências a recursos da Web adicionais, como CSS, script ou arquivos de imagem que também são atendidos por meio de URLs de arquivo.

Recursos web adicionais resolvidos no processo WebView2

As URLs de arquivo são resolvidas nos processos do WebView2. Essa é uma opção mais rápida do que WebResourceRequested, que é resolvida no thread da interface do usuário do processo do aplicativo host.

APIs para carregar conteúdo local navegando até uma URL de arquivo

Exemplo de uma URL de arquivo

Esta seção mostra como é uma URL de arquivo para um caminho de arquivo de conteúdo local de forma independente da plataforma.

Um aplicativo WebView2 precisa codificar URLs de arquivo local usando um file:/// prefixo e encaminhar barras. Por exemplo, para o exemplo Demo To Do, o caminho seria:

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

Para copiar o caminho completo com o prefixo "arquivo" para um arquivo local:

  1. Opcionalmente, clone o repositório Demos para que você tenha uma cópia local. Consulte Etapa 5: Clonar o repositório Demos na instalação da extensão DevTools para Visual Studio Code.

  2. No Microsoft Edge, pressione Ctrl+O para abrir um arquivo. Abra um arquivo local .html , como o arquivo Demos/demo-to-do/index.htmlclonado localmente:

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

    A barra de endereços inicialmente não mostra o file:/// prefixo, mas começa com a letra da unidade:

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

    Barra de endereços do Microsoft Edge inicialmente ocultando o prefixo file:///

  3. Clique na barra Endereço e pressione a tecla Home ou pressione Ctrl+A para selecionar todo o caminho.

    Barra de endereços do Microsoft Edge agora mostrando o prefixo file:///

    Todo o caminho do arquivo, incluindo file:/// , é copiado no buffer da área de transferência, para que você possa colar o caminho completo, incluindo o file:/// prefixo:

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

Veja também:

Exemplo de navegação para uma URL de arquivo

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

Carregando conteúdo local navegando até uma cadeia de caracteres HTML

Outro método para carregar conteúdo local é o NavigateToString método. Essa abordagem carrega o conteúdo no WebView2 diretamente de uma cadeia de caracteres. Isso pode ser útil se você estiver empacotando o conteúdo por meio do código do aplicativo ou se quiser criar dinamicamente o conteúdo.

Outro cenário em que navegar até uma cadeia de caracteres pode ser útil é se você quiser carregar conteúdo que não esteja acessível por meio de uma URL. Por exemplo, se você tiver uma representação na memória de um documento HTML, poderá usar o NavigateToString método para carregar esse conteúdo no controle WebView2. Isso pode ser útil se você quiser evitar a necessidade de gravar o conteúdo em um arquivo ou servidor antes de carregá-lo no controle.

Considerações sobre o carregamento de conteúdo local navegando até uma cadeia de caracteres HTML

APIs DOM baseadas em origem

Um documento carregado usando o NavigateToString método tem sua localização definida como about:blank e sua origem definida como null. Isso significa que as APIs web que dependem de uma origem que está sendo definida, como localStorage ou indexedDB, não podem ser usadas.

APIs DOM que exigem contexto seguro

Algumas APIs Web são limitadas apenas para proteger URLs HTTPS e não estão disponíveis para documentos carregados por meio do NavigateToString método porque sua localização está definida como about:blank. Isso inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() acessar a localização do dispositivo ou Notification.requestPermission() solicitar a permissão do usuário para exibir notificações. Consulte Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local por meio do NavigateToString método, você está fornecendo diretamente o conteúdo como um parâmetro para o método. Isso significa que você está no controle do conteúdo em runtime e pode gerá-lo dinamicamente, se necessário.

Recursos web adicionais

Carregar conteúdo local usando o NavigateToString método não torna possível que o documento resultante faça referência a recursos adicionais da Web, como CSS, imagem ou arquivos de script. O método só permite especificar o conteúdo da cadeia de caracteres do documento HTML. Para fazer referência a recursos web adicionais do documento HTML, use uma das outras abordagens descritas neste artigo ou represente esses recursos Web adicionais embutidos no documento HTML.

Recursos web adicionais resolvidos no processo WebView2

NavigateToString não dá suporte a recursos web adicionais, conforme mencionado acima.

APIs para carregar conteúdo local navegando até uma cadeia de caracteres HTML

Representação de cadeia de caracteres de exemplo de uma página da Web

A seguir está a representação de cadeia de caracteres da página da Web Demonstração para Fazer . A listagem abaixo adicionou o encapsulamento de linha para legibilidade. Na prática, essas linhas são concatenadas em uma única linha longa:

`<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>`

Para obter a cadeia de caracteres acima:

  1. Vá para Demonstração para Fazer.

  2. Clique com o botão direito do mouse na página da Web e selecione Inspecionar para abrir o DevTools.

  3. No Console de DevTools, insira: document.body.parentElement.outerHTML. O Console gera uma representação de cadeia de caracteres da página da Web:

    Uma representação de cadeia de caracteres da página da Web Demonstração para Fazer

Exemplo de navegação para uma cadeia de caracteres HTML

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

Carregando conteúdo local usando o mapeamento de nome do host virtual

Outra maneira de carregar conteúdo local em um controle WebView2 é usar o mapeamento de nome do host virtual. Isso envolve mapear um nome de domínio local para uma pasta local, de modo que, quando o controle WebView2 tentar carregar um recurso para esse domínio, ele carregará o conteúdo do local da pasta local especificado. A origem do documento também será o nome do host virtual.

Essa abordagem permite especificar o acesso entre origens usando o CoreWebView2HostResourceAccessKind enum.

Devido a uma limitação atual, os arquivos de mídia acessados usando um nome de host virtual podem ser lentos para carregar.

Considerações sobre o carregamento de conteúdo local usando o mapeamento de nome do host virtual

APIs DOM baseadas em origem

O conteúdo local carregado por meio do mapeamento de nome do host virtual resulta em um documento que tem uma URL HTTP ou HTTPS e uma origem correspondente. Isso significa que as APIs Web que exigem uma origem, como localStorage ou indexedDB funcionarão, e outros documentos que pertencem à mesma origem poderão usar os dados armazenados. Para obter mais informações, consulte Política de mesma origem no MDN.

APIs DOM que exigem contexto seguro

Algumas APIs Web são limitadas apenas para proteger URLs HTTPS. O uso do mapeamento de nome do host virtual fornece uma URL HTTPS para seu conteúdo local. Isso significa que APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() acessar a localização do dispositivo ou Notification.requestPermission() solicitar a permissão do usuário para exibir notificações estão disponíveis. Consulte Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local por meio de um mapeamento de nome do host virtual, você está mapeando um nome de host virtual para uma pasta local que contém arquivos estáticos em disco. Isso significa que não é possível modificar dinamicamente esse conteúdo local. Isso é diferente de carregar documentos de um servidor Web, em que cada resposta pode ser gerada dinamicamente.

Recursos web adicionais

O conteúdo local carregado por meio do mapeamento de nome do host virtual tem uma URL HTTP ou HTTPS que dá suporte à resolução de URL relativa. Isso significa que o documento carregado pode ter referências a recursos da Web adicionais, como CSS, script ou arquivos de imagem que também são atendidos por meio do mapeamento de nomes de host virtual.

Recursos web adicionais resolvidos no processo WebView2

As URLs de nome do host virtual são resolvidas nos processos do WebView2. Essa é uma opção mais rápida do que WebResourceRequested, que é resolvida no thread da interface do usuário do processo do aplicativo host.

APIs para carregar conteúdo local usando o mapeamento de nome do host virtual

Exemplo de mapeamento de nome do host virtual

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

Carregando conteúdo local manipulando o evento WebResourceRequested

Outra maneira de hospedar conteúdo local em um controle WebView2 é confiando no WebResourceRequested evento. Esse evento é disparado quando o controle tenta carregar um recurso. Você pode usar esse evento para interceptar a solicitação e fornecer o conteúdo local, conforme descrito no gerenciamento personalizado de solicitações de rede.

WebResourceRequested permite personalizar o comportamento do conteúdo local por solicitação. Isso significa que você pode decidir para quais solicitações interceptar e fornecer seu próprio conteúdo e quais solicitações permitir que o controle WebView2 manipule normalmente. No entanto, personalizar o comportamento requer mais código, como mapeamento de host virtual, e requer conhecimento de HTTP, para poder construir uma resposta adequada.

Na perspectiva do WebView2, o recurso terá vindo por meio da rede e o WebView2 seguirá os cabeçalhos definidos pelo aplicativo como parte da resposta. O uso do WebResourceRequested evento também é mais lento do que outras abordagens, devido à comunicação e processamento entre processos necessários para cada solicitação.

Registro de esquema personalizado

Se você quiser usar um esquema personalizado para fazer a Solicitação de Recursos da Web que gera o WebResourceRequested evento, consulte Registro de esquema personalizado em Visão geral dos recursos e APIs do WebView2.

Considerações sobre o carregamento de conteúdo local manipulando o evento WebResourceRequested

APIs DOM baseadas em origem

O conteúdo local carregado por meio WebResourceRequested resulta em um documento que tem uma URL HTTP ou HTTPS e uma origem correspondente. Isso significa que as APIs Web que exigem uma origem, como localStorage ou indexedDB funcionarão, e outros documentos que pertencem à mesma origem poderão usar os dados armazenados. Para obter mais informações, consulte Política de mesma origem no MDN.

APIs DOM que exigem contexto seguro

Algumas APIs Web são limitadas apenas para proteger URLs HTTPS. O uso WebResourceRequested permite que você substitua solicitações de recurso Web da URL HTTPS pelo seu próprio conteúdo local. Isso significa que APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() acessar a localização do dispositivo ou Notification.requestPermission() solicitar a permissão do usuário para exibir notificações estão disponíveis. Consulte Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local por meio WebResourceRequesteddo , você especifica o conteúdo local a ser carregado no manipulador de eventos. Isso significa que você está no controle do conteúdo em runtime e pode gerá-lo dinamicamente, se necessário.

Recursos web adicionais

WebResourceRequested modifica o conteúdo carregado por meio de URLs HTTP ou HTTPS, que dão suporte à resolução de URL relativa. Isso significa que o documento resultante pode ter referências a recursos da Web adicionais, como CSS, script ou arquivos de imagem que também são atendidos por meio WebResourceRequestedde .

Recursos web adicionais resolvidos no processo WebView2

Ao carregar conteúdo por meio de uma URL de arquivo ou um mapeamento de nome de host virtual, a resolução acontece nos processos do WebView2. No entanto, o WebResourceRequested evento é gerado no thread da interface do usuário do WebView2 do processo do aplicativo host, o que pode levar a um carregamento mais lento do documento resultante.

  1. O WebView2 faz uma primeira pausa no carregamento da página da Web para aguardar o envio do evento para o processo do aplicativo host.
  2. O WebView2 aguarda que o thread da interface do usuário esteja disponível.
  3. WebView2 aguarda o código do aplicativo para lidar com o evento.

Isso pode levar algum tempo. Certifique-se de limitar as chamadas AddWebResourceRequestedFilter para apenas os recursos web que devem gerar o WebResourceRequested evento.

APIs para carregar conteúdo local manipulando o evento WebResourceRequested

Exemplo de manipulação do evento 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", "");
    }
};

Confira também