Compartilhar via


Trabalhar com conteúdo local em aplicações WebView2

Além de carregar conteúdo remoto, o conteúdo também pode ser carregado localmente para o WebView2. Existem várias abordagens que podem ser utilizadas para carregar conteúdo local para um controlo WebView2, incluindo:

  • Navegar para um URL de ficheiro.
  • Navegar para uma cadeia HTML.
  • Mapeamento de nomes de anfitrião virtual.
  • A processar o WebResourceRequested evento.

Estas abordagens são descritas abaixo.

Selecionar uma abordagem

As várias formas de carregar conteúdo local para um controlo WebView2 suportam os seguintes cenários:

Cenário Ao navegar para um URL de ficheiro Ao navegar para uma cadeia HTML Ao utilizar o mapeamento de nomes de anfitrião virtual Ao utilizar WebResourceRequested
APIs DOM baseadas na origem ✔️ ✔️ ✔️
APIs DOM que requerem contexto seguro ✔️ ✔️
Conteúdo dinâmico ✔️ ✔️
Recursos Web adicionais ✔️ ✔️ ✔️
Recursos Web adicionais resolvidos no processo WebView2 ✔️ ✔️

Estes cenários são descritos mais detalhadamente abaixo.

Carregar conteúdo local ao navegar para um URL de ficheiro

O WebView2 permite navegação para URLs de ficheiros, para carregar HTML básico ou um PDF. Esta é a abordagem mais simples e eficiente para carregar conteúdo local. No entanto, é menos flexível do que as outras abordagens. Tal como num browser, os URLs de ficheiros são limitados em algumas capacidades:

  • O documento tem uma origem exclusiva para o respetivo caminho de ficheiro. Isto significa que as APIs Web que requerem uma origem, como localStorage ou indexedDB irão funcionar, mas os dados armazenados não estarão disponíveis para outros documentos locais carregados a partir de outros caminhos de ficheiro.

  • Algumas APIs Web estão limitadas apenas a URLs HTTPS seguros e não estão disponíveis para documentos carregados por URLs de ficheiro. Isto inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() aceder à localização do dispositivo ou Notification.requestPermission() pedir a permissão do utilizador para apresentar notificações.

  • Para cada recurso, o caminho completo tem de ser especificado.

  • Para permitir referências a outros ficheiros locais a partir de URIs de ficheiros ou para apresentar ficheiros XML com transformações XSL aplicadas, pode definir o argumento do --allow-file-access-from-files browser. Veja CoreWebView2EnvironmentOptions.AdditionalBrowserArguments Property (Propriedade CoreWebView2EnvironmentOptions.AdditionalBrowserArguments).

Considerações para carregar conteúdo local ao navegar para um URL de ficheiro

Os URLs dos ficheiros comportam-se como no browser. Por exemplo, não pode criar um XMLHttpRequest (XHR) num URL de ficheiro, porque não está a trabalhar no contexto de uma página Web.

Tem de especificar o caminho completo do ficheiro para cada recurso. Por exemplo:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
Recursos de várias origens

Ao especificar um URL de ficheiro, a aplicação navega para um ficheiro no disco e não para um domínio na rede. Como resultado, não é possível utilizar recursos de várias origens no documento resultante.

APIs DOM baseadas na origem

Um documento carregado através de um URL de ficheiro tem uma origem exclusiva do respetivo caminho de ficheiro, tal como no browser. AS APIs Web que necessitam de uma origem, como localStorage ou indexedDB irão funcionar. No entanto, diferentes documentos carregados a partir de URLs de ficheiros diferentes não são considerados como provenientes da mesma origem e não terão acesso aos mesmos dados armazenados.

APIs DOM que requerem contexto seguro

Algumas APIs Web estão limitadas apenas a URLs HTTPS seguros e não estão disponíveis para documentos carregados por URLs de ficheiro. Isto inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() aceder à localização do dispositivo ou Notification.requestPermission() pedir a permissão do utilizador para apresentar notificações. Veja Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar um documento através de um URL de ficheiro, o conteúdo do documento provém de ficheiros estáticos no disco. Isto significa que não é possível modificar dinamicamente este conteúdo local. Isto é diferente de carregar documentos a partir de um servidor Web, onde cada resposta pode ser gerada dinamicamente.

Recursos Web adicionais

A resolução de URL relativa também funciona para documentos carregados através de um URL de ficheiro. Isto significa que o documento carregado pode ter referências a recursos Web adicionais, como CSS, script ou ficheiros de imagem, que também são servidos através de URLs de ficheiro.

Recursos Web adicionais resolvidos no processo WebView2

Os URLs de ficheiro são resolvidos nos processos webView2. Esta é uma opção mais rápida do que WebResourceRequested, que é resolvida no thread de IU do processo da aplicação anfitriã.

APIs para carregar conteúdo local ao navegar para um URL de ficheiro

Exemplo de um URL de ficheiro

Esta secção mostra o aspeto de um URL de ficheiro para um caminho de ficheiro de conteúdo local de uma forma independente da plataforma.

Uma aplicação WebView2 tem de codificar URLs de ficheiro local com um file:/// prefixo e 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 "ficheiro" para um ficheiro local:

  1. Opcionalmente, clone o repositório Demos para que tenha uma cópia local. Veja Passo 5: Clonar o repositório Demos em Instalar a extensão DevTools para o Visual Studio Code.

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

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

    Inicialmente, a Barra de endereço não mostra o file:/// prefixo, mas começa com a letra de unidade:

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

    A barra de endereço do Microsoft Edge oculta inicialmente o prefixo file:///

  3. Clique na Barra de endereço e, em seguida, prima a tecla Base ou prima Ctrl+A para selecionar todo o caminho.

    A barra de endereço do Microsoft Edge mostra agora o prefixo file:///

    Todo o caminho do ficheiro, incluindo file:/// , é copiado para a memória intermédia da área de transferência, para que 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 um URL de ficheiro

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

Carregar conteúdo local ao navegar para uma cadeia HTML

Outro método para carregar conteúdo local é o NavigateToString método . Esta abordagem carrega o conteúdo para o WebView2 diretamente a partir de uma cadeia. Isto pode ser útil se estiver a empacotar o conteúdo através do código da aplicação ou se quiser criar dinamicamente o conteúdo.

Outro cenário em que navegar para uma cadeia pode ser útil se quiser carregar conteúdo que não esteja acessível através de um URL. Por exemplo, se tiver uma representação dentro da memória de um documento HTML, pode utilizar o NavigateToString método para carregar esse conteúdo para o controlo WebView2. Isto pode ser útil se quiser evitar a necessidade de escrever o conteúdo num ficheiro ou servidor antes de o carregar para o controlo.

Considerações para carregar conteúdo local ao navegar para uma cadeia HTML

A cadeia de conteúdo HTML que é transmitida para o NavigateToString método tem um limite de tamanho de 2 MB. Este limite de tamanho pode ser fácil de exceder quando a cadeia inclui recursos adicionais indicados. Se este limite de tamanho for excedido, é devolvido um erro: "O valor não se enquadra no intervalo esperado".

APIs DOM baseadas na origem

Um documento carregado com o método tem a NavigateToString localização definida como e a about:blank origem definida como null. Isto significa que as APIs Web que dependem de uma origem a ser definida, como localStorage ou indexedDB, não podem ser utilizadas.

APIs DOM que requerem contexto seguro

Algumas APIs Web estão limitadas apenas a URLs HTTPS seguros e não estão disponíveis para documentos carregados através do método porque a NavigateToString localização está definida como about:blank. Isto inclui APIs como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() aceder à localização do dispositivo ou Notification.requestPermission() pedir a permissão do utilizador para apresentar notificações. Veja Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local através do NavigateToString método , está a fornecer diretamente o conteúdo como um parâmetro para o método . Isto significa que controla o conteúdo no runtime e pode gerá-lo dinamicamente, se necessário.

Recursos Web adicionais

Carregar conteúdo local com o NavigateToString método não permite que o documento resultante faça referência a recursos Web adicionais, como CSS, ficheiros de imagem ou script. O método só lhe permite especificar o conteúdo da cadeia de carateres do documento HTML. Para referenciar recursos Web adicionais do seu documento HTML, utilize uma das outras abordagens descritas neste artigo ou represente esses recursos Web adicionais inline no documento HTML.

Recursos Web adicionais resolvidos no processo WebView2

NavigateToString não suporta recursos Web adicionais, conforme mencionado acima.

APIs para carregar conteúdo local ao navegar para uma cadeia HTML

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

Segue-se a representação da cadeia de carateres da página Web Demo To Do . A listagem abaixo adicionou moldagem de linhas para legibilidade. Na prática, estas linhas são concatenadas numa ú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 acima:

  1. Aceda a Demonstração a Fazer.

  2. Clique com o botão direito do rato na página Web e, em seguida, selecione Inspecionar para abrir DevTools.

  3. Na Consola do DevTools, introduza: document.body.parentElement.outerHTML. A Consola produz uma representação de cadeia da página Web:

    Uma representação de cadeia da página Web Demo To Do

Exemplo de navegação para uma cadeia HTML

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

Carregar conteúdo local com o mapeamento de nomes de anfitrião virtual

Outra forma de carregar conteúdo local num controlo WebView2 é utilizar o mapeamento de nomes de anfitrião virtual. Isto envolve mapear um nome de domínio local para uma pasta local, para que, quando o controlo WebView2 tentar carregar um recurso para esse domínio, carregue o conteúdo a partir da localização da pasta local especificada. A origem do documento também será o nome do anfitrião virtual.

Esta abordagem permite-lhe especificar o acesso entre origens através da enumeração CoreWebView2HostResourceAccessKind .

Devido a uma limitação atual, os ficheiros multimédia que são acedidos com um nome de anfitrião virtual podem ser lentos a carregar.

Considerações para carregar conteúdo local através do mapeamento de nomes de anfitrião virtual

APIs DOM baseadas na origem

O conteúdo local carregado através do mapeamento de nomes de anfitrião virtual resulta num documento que tem um URL HTTP ou HTTPS e uma origem correspondente. Isto significa que as APIs Web que necessitam de uma origem, como localStorage ou indexedDB irão funcionar, e outros documentos que pertencem à mesma origem poderão utilizar os dados armazenados. Para obter mais informações, veja Política de origem idêntica no MDN.

APIs DOM que requerem contexto seguro

Algumas APIs Web estão limitadas apenas a URLs HTTPS seguros. A utilização do mapeamento de nomes de anfitrião virtual fornece um URL HTTPS para o seu conteúdo local. Isto significa que as APIs, como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() aceder à localização do dispositivo ou Notification.requestPermission() pedir a permissão do utilizador para apresentar notificações estão disponíveis. Veja Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local através de um mapeamento de nomes de anfitrião virtual, está a mapear um nome de anfitrião virtual para uma pasta local que contém ficheiros estáticos no disco. Isto significa que não é possível modificar dinamicamente este conteúdo local. Isto é diferente de carregar documentos a partir de um servidor Web, onde cada resposta pode ser gerada dinamicamente.

Recursos Web adicionais

O conteúdo local carregado através do mapeamento de nomes de anfitrião virtual tem um URL HTTP ou HTTPS que suporta a resolução de URL relativa. Isto significa que o documento carregado pode ter referências a recursos Web adicionais, como CSS, script ou ficheiros de imagem, que também são servidos através do mapeamento de nomes de anfitrião virtual.

Recursos Web adicionais resolvidos no processo WebView2

Os URLs do nome do anfitrião virtual são resolvidos em processos WebView2. Esta é uma opção mais rápida do que WebResourceRequested, que é resolvida no thread de IU do processo da aplicação anfitriã.

APIs para carregar conteúdo local através do mapeamento de nomes de anfitrião virtual

Exemplo de mapeamento de nomes de anfitrião virtual

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

Carregar conteúdo local ao processar o evento WebResourceRequested

Outra forma de alojar conteúdo local num controlo WebView2 é dependendo do WebResourceRequested evento. Este evento é acionado quando o controlo tenta carregar um recurso. Pode utilizar este evento para intercetar o pedido e fornecer o conteúdo local, conforme descrito em Gestão personalizada de pedidos de rede.

WebResourceRequested permite-lhe personalizar o comportamento dos conteúdos locais numa base por pedido. Isto significa que pode decidir para que pedidos intercetar e fornecer o seu próprio conteúdo e quais os pedidos para permitir que o controlo WebView2 processe normalmente. No entanto, personalizar o comportamento requer mais código, como o mapeamento de anfitriões virtuais, e requer conhecimento de HTTP, para conseguir construir uma resposta adequada.

Na perspetiva do WebView2, o recurso terá vindo através da rede e o WebView2 irá cumprir os cabeçalhos definidos pela aplicação como parte da resposta. A utilização do WebResourceRequested evento também é mais lenta do que outras abordagens, devido à comunicação e processamento entre processos necessários para cada pedido.

Registo de esquema personalizado

Se quiser utilizar um esquema personalizado para fazer o Pedido de Recurso Web que gera o WebResourceRequested evento, veja Registo de esquema personalizado em Descrição geral das funcionalidades e APIs webView2.

Considerações para carregar conteúdo local ao processar o evento WebResourceRequested

APIs DOM baseadas na origem

Conteúdo local carregado através de WebResourceRequested resultados num documento que tem um URL HTTP ou HTTPS e uma origem correspondente. Isto significa que as APIs Web que necessitam de uma origem, como localStorage ou indexedDB irão funcionar, e outros documentos que pertencem à mesma origem poderão utilizar os dados armazenados. Para obter mais informações, veja Política de origem idêntica no MDN.

APIs DOM que requerem contexto seguro

Algumas APIs Web estão limitadas apenas a URLs HTTPS seguros. A utilização WebResourceRequested permite-lhe substituir pedidos de recursos Web de URL HTTPS pelo seu próprio conteúdo local. Isto significa que as APIs, como navigator.mediaDevices.getUserMedia() adquirir vídeo ou som, navigator.geolocation.getCurrentPosition() aceder à localização do dispositivo ou Notification.requestPermission() pedir a permissão do utilizador para apresentar notificações estão disponíveis. Veja Contextos seguros no MDN para obter mais informações.

Conteúdo dinâmico

Ao carregar conteúdo local através WebResourceRequestedde , especifique o conteúdo local a carregar no processador de eventos. Isto significa que controla o conteúdo no runtime e pode gerá-lo dinamicamente, se necessário.

Recursos Web adicionais

WebResourceRequested modifica o conteúdo carregado através de URLs HTTP ou HTTPS, que suportam a resolução relativa do URL. Isto significa que o documento resultante pode ter referências a recursos Web adicionais, como CSS, script ou ficheiros de imagem que também são servidos através do WebResourceRequested.

Recursos Web adicionais resolvidos no processo WebView2

Ao carregar conteúdo através de um URL de ficheiro ou de um mapeamento de nome de anfitrião virtual, a resolução ocorre nos processos webView2. No entanto, o WebResourceRequested evento é gerado no thread da IU webView2 do processo da aplicação anfitriã, o que pode levar a um carregamento mais lento do documento resultante.

  1. Primeiro, o WebView2 interrompe o carregamento da página Web para aguardar que o evento seja enviado para o processo da aplicação anfitriã.
  2. Em seguida, o WebView2 aguarda que o thread de IU esteja disponível.
  3. Em seguida, o WebView2 aguarda que o código da aplicação processe o evento.

Esta ação pode demorar algum tempo. Certifique-se de que limita as chamadas para AddWebResourceRequestedFilter apenas para os recursos Web que têm de gerar o WebResourceRequested evento.

APIs para carregar conteúdo local ao processar o evento WebResourceRequested

Exemplo de processamento 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