Compartir a través de


Trabajar con contenido local en aplicaciones WebView2

Además de cargar contenido remoto, el contenido también se puede cargar localmente en WebView2. Hay varios enfoques que se pueden usar para cargar contenido local en un control WebView2, entre los que se incluyen:

  • Vaya a una dirección URL de archivo.
  • Navegar a una cadena HTML.
  • Asignación de nombres de host virtual.
  • Control del WebResourceRequested evento.

Estos enfoques se describen a continuación.

Selección de un enfoque

Las distintas formas de cargar contenido local en un control WebView2 admiten los siguientes escenarios:

Escenario Al navegar a una dirección URL de archivo Al navegar a una cadena HTML Mediante la asignación de nombres de host virtual Mediante el uso de WebResourceRequested
API DOM basadas en origen ✔️ ✔️ ✔️
API dom que requieren contexto seguro ✔️ ✔️
Contenido dinámico ✔️ ✔️
Recursos web adicionales ✔️ ✔️ ✔️
Recursos web adicionales resueltos en el proceso WebView2 ✔️ ✔️

Estos escenarios se describen con más detalle a continuación.

Carga de contenido local navegando a una dirección URL de archivo

WebView2 permite la navegación a las direcciones URL de archivo, para cargar HTML básico o un PDF. Este es el enfoque más sencillo y eficaz para cargar contenido local. Sin embargo, es menos flexible que los demás enfoques. Al igual que en un explorador web, las direcciones URL de archivo están limitadas en algunas funcionalidades:

  • El documento tiene un origen que es único en su ruta de acceso de archivo. Esto significa que las API web que requieren un origen como localStorage o indexedDB funcionarán, pero los datos almacenados no estarán disponibles para otros documentos locales cargados desde otras rutas de acceso de archivo.

  • Algunas API web se limitan solo a las direcciones URL HTTPS seguras y no están disponibles para los documentos cargados por direcciones URL de archivo. Esto incluye API como navigator.mediaDevices.getUserMedia() adquirir vídeo o sonido, navigator.geolocation.getCurrentPosition() acceder a la ubicación del dispositivo o Notification.requestPermission() solicitar el permiso del usuario para mostrar notificaciones.

  • Para cada recurso, se debe especificar la ruta de acceso completa.

  • Para permitir referencias a otros archivos locales desde uri de archivo o para mostrar archivos XML con transformaciones XSL aplicadas, puede establecer el argumento del --allow-file-access-from-files explorador. Vea CoreWebView2EnvironmentOptions.AdditionalBrowserArguments (propiedad).

Consideraciones para cargar contenido local navegando a una dirección URL de archivo

Las direcciones URL de archivo se comportan como en el explorador. Por ejemplo, no puede crear una XMLHttpRequest (XHR) en una dirección URL de archivo, porque no está trabajando en el contexto de una página web.

Debe especificar la ruta de acceso completa del archivo para cada recurso. Por ejemplo:

file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html
Recursos entre orígenes

Al especificar una dirección URL de archivo, la aplicación navega a un archivo en disco, no a un dominio de la red. Como resultado, no es posible usar recursos entre orígenes en el documento resultante.

API DOM basadas en origen

Un documento cargado a través de una dirección URL de archivo tiene un origen que es único para su ruta de acceso de archivo, al igual que en el explorador. Las API web que requieren un origen como localStorage o indexedDB funcionarán. Sin embargo, los distintos documentos cargados de direcciones URL de archivo diferentes no se consideran del mismo origen y no tendrán acceso a los mismos datos almacenados.

API dom que requieren contexto seguro

Algunas API web se limitan solo a las direcciones URL HTTPS seguras y no están disponibles para los documentos cargados por direcciones URL de archivo. Esto incluye API como navigator.mediaDevices.getUserMedia() adquirir vídeo o sonido, navigator.geolocation.getCurrentPosition() acceder a la ubicación del dispositivo o Notification.requestPermission() solicitar el permiso del usuario para mostrar notificaciones. Consulte Contextos seguros en MDN para obtener más información.

Contenido dinámico

Al cargar un documento a través de una dirección URL de archivo, el contenido del documento procede de un archivo estático en el disco. Esto significa que no es posible modificar dinámicamente este contenido local. Esto es diferente de cargar documentos desde un servidor web, donde cada respuesta se puede generar dinámicamente.

Recursos web adicionales

La resolución de direcciones URL relativas también funciona para los documentos cargados a través de una dirección URL de archivo. Esto significa que el documento cargado puede tener referencias a recursos web adicionales como CSS, scripts o archivos de imagen que también se proporcionan a través de direcciones URL de archivo.

Recursos web adicionales resueltos en el proceso WebView2

Las direcciones URL de archivo se resuelven en procesos de WebView2. Se trata de una opción más rápida que WebResourceRequested, que se resuelve en el subproceso de interfaz de usuario del proceso de aplicación host.

API para cargar contenido local navegando a una dirección URL de archivo

Ejemplo de una dirección URL de archivo

En esta sección se muestra el aspecto de una dirección URL de archivo para una ruta de acceso de archivo de contenido local de forma independiente de la plataforma.

Una aplicación WebView2 debe codificar direcciones URL de archivo local con un file:/// prefijo y barras diagonales. Por ejemplo, para el ejemplo Demo To Do, la ruta de acceso sería:

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

Para copiar la ruta de acceso completa con el prefijo "file" para un archivo local:

  1. Opcionalmente, clone el repositorio Demos para que tenga una copia local. Vea Paso 5: Clonar el repositorio de demostraciones en Instalación de la extensión DevTools para Visual Studio Code.

  2. En Microsoft Edge, presione Ctrl+O para abrir un archivo. Abra un archivo local .html , como el archivo Demos/demo-to-do/index.htmlclonado localmente:

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

    La barra de direcciones no muestra inicialmente el file:/// prefijo, pero comienza con la letra de unidad:

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

    Barra de direcciones de Microsoft Edge ocultando inicialmente el prefijo file:///

  3. Haga clic en la barra Dirección y, a continuación, presione la tecla Inicio o presione Ctrl+A para seleccionar toda la ruta de acceso.

    Barra de direcciones de Microsoft Edge que muestra ahora el prefijo file:///

    Toda la ruta de acceso del archivo, incluida file:/// , se copia en el búfer del Portapapeles, por lo que puede pegar la ruta de acceso completa, incluido el file:/// prefijo:

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

Vea también:

Ejemplo de navegación a una dirección URL de archivo

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

Carga de contenido local navegando a una cadena HTML

Otro método para cargar contenido local es el NavigateToString método . Este enfoque carga el contenido en WebView2 directamente desde una cadena. Esto puede ser útil si va a empaquetar el contenido a través del código de la aplicación o si desea crear dinámicamente el contenido.

Otro escenario en el que puede ser útil navegar a una cadena es si desea cargar contenido al que no se puede acceder a través de una dirección URL. Por ejemplo, si tiene una representación en memoria de un documento HTML, podría usar el NavigateToString método para cargar ese contenido en el control WebView2. Esto puede ser útil si quiere evitar la necesidad de escribir el contenido en un archivo o servidor antes de cargarlo en el control.

Consideraciones para cargar contenido local navegando a una cadena HTML

La cadena de contenido HTML que se pasa al NavigateToString método tiene un límite de tamaño de 2 MB. Este límite de tamaño puede ser fácil de superar cuando la cadena incluye recursos adicionales incluidos. Si se supera este límite de tamaño, se devuelve un error: "El valor no está dentro del intervalo esperado".

API DOM basadas en origen

Un documento cargado mediante el NavigateToString método tiene su ubicación establecida en about:blank y su origen establecido en null. Esto significa que las API web que dependen de un origen que se está definiendo, como localStorage o indexedDB, no se pueden usar.

API dom que requieren contexto seguro

Algunas API web se limitan solo a las direcciones URL HTTPS seguras y no están disponibles para los documentos cargados a través del NavigateToString método porque su ubicación está establecida en about:blank. Esto incluye API como navigator.mediaDevices.getUserMedia() adquirir vídeo o sonido, navigator.geolocation.getCurrentPosition() acceder a la ubicación del dispositivo o Notification.requestPermission() solicitar el permiso del usuario para mostrar notificaciones. Consulte Contextos seguros en MDN para obtener más información.

Contenido dinámico

Al cargar contenido local a través del NavigateToString método , se proporciona directamente el contenido como parámetro al método . Esto significa que tiene el control del contenido en tiempo de ejecución y puede generarlo dinámicamente si es necesario.

Recursos web adicionales

La carga de contenido local mediante el NavigateToString método no permite que el documento resultante haga referencia a recursos web adicionales, como CSS, imágenes o archivos de script. El método solo permite especificar el contenido de cadena del documento HTML. Para hacer referencia a recursos web adicionales del documento HTML, use uno de los otros enfoques descritos en este artículo o represente esos recursos web adicionales insertados en el documento HTML.

Recursos web adicionales resueltos en el proceso WebView2

NavigateToString no admite recursos web adicionales, como se mencionó anteriormente.

API para cargar contenido local navegando a una cadena HTML

Representación de cadena de ejemplo de una página web

A continuación se muestra la representación de cadena de la página web Demo To Do . En la lista siguiente se ha agregado ajuste de línea para mejorar la legibilidad. En la práctica, estas líneas se concatenan en una sola línea larga:

`<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 obtener la cadena anterior:

  1. Vaya a Demostración a hacer.

  2. Haga clic con el botón derecho en la página web y seleccione Inspeccionar para abrir DevTools.

  3. En la consola de DevTools, escriba: document.body.parentElement.outerHTML. La consola genera una representación de cadena de la página web:

    Representación de cadena de la página web De demostración a realizar

Ejemplo de navegación a una cadena HTML

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

Carga de contenido local mediante la asignación de nombres de host virtual

Otra manera de cargar contenido local en un control WebView2 es usar la asignación de nombres de host virtual. Esto implica la asignación de un nombre de dominio local a una carpeta local, de modo que cuando el control WebView2 intenta cargar un recurso para ese dominio, cargará el contenido desde la ubicación de carpeta local especificada en su lugar. El origen del documento también será el nombre de host virtual.

Este enfoque le permite especificar el acceso entre orígenes mediante la CoreWebView2HostResourceAccessKind enumeración .

Debido a una limitación actual, los archivos multimedia a los que se accede mediante un nombre de host virtual pueden ser lentos de cargar.

Consideraciones para cargar contenido local mediante la asignación de nombres de host virtual

API DOM basadas en origen

El contenido local cargado a través de la asignación de nombres de host virtual da como resultado un documento que tiene una dirección URL HTTP o HTTPS y un origen correspondiente. Esto significa que las API web que requieren un origen como localStorage o indexedDB funcionarán, y otros documentos que pertenecen al mismo origen podrán usar los datos almacenados. Para obtener más información, consulte Directiva del mismo origen en MDN.

API dom que requieren contexto seguro

Algunas API web se limitan solo a las direcciones URL HTTPS seguras. El uso de la asignación de nombres de host virtual proporciona una dirección URL HTTPS para el contenido local. Esto significa que las API como navigator.mediaDevices.getUserMedia() adquirir vídeo o sonido, navigator.geolocation.getCurrentPosition() acceder a la ubicación del dispositivo o Notification.requestPermission() solicitar el permiso del usuario para mostrar notificaciones están disponibles. Consulte Contextos seguros en MDN para obtener más información.

Contenido dinámico

Al cargar contenido local a través de una asignación de nombres de host virtual, se asigna un nombre de host virtual a una carpeta local que contiene archivos estáticos en el disco. Esto significa que no es posible modificar dinámicamente este contenido local. Esto es diferente de cargar documentos desde un servidor web, donde cada respuesta se puede generar dinámicamente.

Recursos web adicionales

El contenido local que se carga a través de la asignación de nombres de host virtual tiene una dirección URL HTTP o HTTPS que admite la resolución relativa de direcciones URL. Esto significa que el documento cargado puede tener referencias a recursos web adicionales, como CSS, scripts o archivos de imagen que también se sirven a través de la asignación de nombres de host virtual.

Recursos web adicionales resueltos en el proceso WebView2

Las direcciones URL del nombre de host virtual se resuelven en los procesos de WebView2. Se trata de una opción más rápida que WebResourceRequested, que se resuelve en el subproceso de interfaz de usuario del proceso de aplicación host.

API para cargar contenido local mediante la asignación de nombres de host virtual

Ejemplo de asignación de nombres de host virtual

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

Carga de contenido local mediante el control del evento WebResourceRequested

Otra manera de hospedar contenido local en un control WebView2 es confiar en el WebResourceRequested evento . Este evento se desencadena cuando el control intenta cargar un recurso. Puede usar este evento para interceptar la solicitud y proporcionar el contenido local, como se describe en Administración personalizada de solicitudes de red.

WebResourceRequested permite personalizar el comportamiento del contenido local por solicitud. Esto significa que puede decidir para qué solicitudes interceptar y proporcionar su propio contenido y para qué solicitudes permitir que el control WebView2 controle con normalidad. Sin embargo, la personalización del comportamiento requiere más código, como la asignación de host virtual, y requiere conocimientos de HTTP para poder construir una respuesta adecuada.

Desde la perspectiva de WebView2, el recurso habrá venido a través de la red y WebView2 se adhiere a los encabezados que establece la aplicación como parte de la respuesta. El uso del WebResourceRequested evento también es más lento que otros enfoques, debido a la comunicación y el procesamiento entre procesos necesarios para cada solicitud.

Registro de esquema personalizado

Si desea usar un esquema personalizado para realizar la solicitud de recursos web que genera el WebResourceRequested evento, consulte Registro de esquemas personalizados en Información general sobre las características y las API de WebView2.

Consideraciones para cargar contenido local controlando el evento WebResourceRequested

API DOM basadas en origen

El contenido local cargado a través de WebResourceRequested da como resultado un documento que tiene una dirección URL HTTP o HTTPS y un origen correspondiente. Esto significa que las API web que requieren un origen como localStorage o indexedDB funcionarán, y otros documentos que pertenecen al mismo origen podrán usar los datos almacenados. Para obtener más información, consulte Directiva del mismo origen en MDN.

API dom que requieren contexto seguro

Algunas API web se limitan solo a las direcciones URL HTTPS seguras. El uso WebResourceRequested le permite reemplazar las solicitudes de recursos web de dirección URL HTTPS por su propio contenido local. Esto significa que las API como navigator.mediaDevices.getUserMedia() adquirir vídeo o sonido, navigator.geolocation.getCurrentPosition() acceder a la ubicación del dispositivo o Notification.requestPermission() solicitar el permiso del usuario para mostrar notificaciones están disponibles. Consulte Contextos seguros en MDN para obtener más información.

Contenido dinámico

Al cargar contenido local a través de WebResourceRequested, especifique el contenido local que se va a cargar en el controlador de eventos. Esto significa que tiene el control del contenido en tiempo de ejecución y puede generarlo dinámicamente si es necesario.

Recursos web adicionales

WebResourceRequested modifica el contenido cargado a través de direcciones URL HTTP o HTTPS, que admiten la resolución relativa de direcciones URL. Esto significa que el documento resultante puede tener referencias a recursos web adicionales, como CSS, scripts o archivos de imagen que también se proporcionan a través de WebResourceRequested.

Recursos web adicionales resueltos en el proceso WebView2

Al cargar contenido a través de una dirección URL de archivo o una asignación de nombres de host virtual, la resolución se produce en los procesos de WebView2. Sin embargo, el WebResourceRequested evento se genera en el subproceso de interfaz de usuario WebView2 del proceso de la aplicación host, lo que puede provocar una carga más lenta del documento resultante.

  1. WebView2 primero pausa la carga de la página web para esperar a que el evento se envíe al proceso de la aplicación host.
  2. WebView2 espera a que el subproceso de la interfaz de usuario esté disponible.
  3. WebView2 espera a que el código de la aplicación controle el evento.

Esto puede tardar algún tiempo. Asegúrese de limitar las llamadas a AddWebResourceRequestedFilter solo los recursos web que deben generar el WebResourceRequested evento.

API para cargar contenido local controlando el evento WebResourceRequested

Ejemplo de control del 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", "");
    }
};

Vea también