Partager via


Utilisation du contenu local dans les applications WebView2

Outre le chargement du contenu distant, le contenu peut également être chargé localement dans WebView2. Il existe plusieurs approches qui peuvent être utilisées pour charger du contenu local dans un contrôle WebView2, notamment :

  • Navigation vers une URL de fichier.
  • Navigation vers une chaîne HTML.
  • Mappage du nom d’hôte virtuel.
  • Gestion de l’événement WebResourceRequested .

Ces approches sont décrites ci-dessous.

Sélection d’une approche

Les différentes façons de charger du contenu local dans un contrôle WebView2 prennent en charge les scénarios suivants :

Scénario En accédant à une URL de fichier En accédant à une chaîne HTML À l’aide du mappage de nom d’hôte virtuel En utilisant WebResourceRequested
API DOM basées sur l’origine ✔️ ✔️ ✔️
API DOM nécessitant un contexte sécurisé ✔️ ✔️
Contenu dynamique ✔️ ✔️
Ressources web supplémentaires ✔️ ✔️ ✔️
Ressources web supplémentaires résolues dans le processus WebView2 ✔️ ✔️

Ces scénarios sont décrits plus en détail ci-dessous.

Chargement de contenu local en accédant à une URL de fichier

WebView2 permet de naviguer vers les URL de fichier, de charger du code HTML de base ou un FICHIER PDF. Il s’agit de l’approche la plus simple et la plus efficace pour charger du contenu local. Toutefois, il est moins souple que les autres approches. Comme dans un navigateur web, les URL de fichier sont limitées dans certaines fonctionnalités :

  • Le document a une origine propre à son chemin d’accès de fichier. Cela signifie que les API web qui nécessitent une origine telle que localStorage ou indexedDB fonctionnent, mais que les données stockées ne seront pas disponibles pour d’autres documents locaux chargés à partir d’autres chemins de fichiers.

  • Certaines API web sont limitées aux URL HTTPS sécurisées uniquement et ne sont pas disponibles pour les documents chargés par les URL de fichier. Cela inclut les API telles que navigator.mediaDevices.getUserMedia() pour acquérir de la vidéo ou du son, navigator.geolocation.getCurrentPosition() pour accéder à l’emplacement de l’appareil ou Notification.requestPermission() pour demander à l’utilisateur l’autorisation d’afficher des notifications.

  • Pour chaque ressource, le chemin d’accès complet doit être spécifié.

  • Pour autoriser les références à d’autres fichiers locaux à partir d’URI de fichier, ou pour afficher des fichiers XML avec des transformations XSL appliquées, vous pouvez définir l’argument du --allow-file-access-from-files navigateur. Consultez CoreWebView2EnvironmentOptions.AdditionalBrowserArguments, propriété.

Considérations relatives au chargement du contenu local en accédant à une URL de fichier

Les URL de fichier se comportent comme dans le navigateur. Par exemple, vous ne pouvez pas créer ( XMLHttpRequest XHR) dans une URL de fichier, car vous ne travaillez pas dans le contexte d’une page web.

Vous devez spécifier le chemin d’accès complet du fichier, pour chaque ressource. Par exemple :

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

Lorsque vous spécifiez une URL de fichier, l’application accède à un fichier sur disque, et non à un domaine sur le réseau. Par conséquent, il n’est pas possible d’utiliser des ressources cross-origin dans le document obtenu.

API DOM basées sur l’origine

Un document chargé via une URL de fichier a une origine unique à son chemin d’accès, comme dans le navigateur. Les API web qui nécessitent une origine telle que localStorage ou indexedDB fonctionnent. Toutefois, les différents documents chargés à partir d’URL de fichier différentes ne sont pas considérés comme provenant de la même origine et n’auront pas accès aux mêmes données stockées.

API DOM nécessitant un contexte sécurisé

Certaines API web sont limitées aux URL HTTPS sécurisées uniquement et ne sont pas disponibles pour les documents chargés par les URL de fichier. Cela inclut les API telles que navigator.mediaDevices.getUserMedia() pour acquérir de la vidéo ou du son, navigator.geolocation.getCurrentPosition() pour accéder à l’emplacement de l’appareil ou Notification.requestPermission() pour demander à l’utilisateur l’autorisation d’afficher des notifications. Pour plus d’informations, consultez Sécuriser les contextes sur MDN.

Contenu dynamique

Lors du chargement d’un document via une URL de fichier, le contenu du document provient d’un fichier statique sur disque. Cela signifie qu’il n’est pas possible de modifier dynamiquement ce contenu local. Cela diffère du chargement de documents à partir d’un serveur web, où chaque réponse peut être générée dynamiquement.

Ressources web supplémentaires

La résolution d’URL relative fonctionne également pour les documents chargés via une URL de fichier. Cela signifie que le document chargé peut avoir des références à des ressources web supplémentaires, telles que des fichiers CSS, de script ou d’image, qui sont également servis via des URL de fichier.

Ressources web supplémentaires résolues dans le processus WebView2

Les URL de fichier sont résolues dans les processus WebView2. Il s’agit d’une option plus rapide que WebResourceRequested, qui se résout dans le thread d’interface utilisateur du processus de l’application hôte.

API pour le chargement de contenu local en accédant à une URL de fichier

Exemple d’URL de fichier

Cette section montre à quoi ressemble une URL de fichier pour un chemin d’accès de fichier de contenu local de manière indépendante de la plateforme.

Une application WebView2 doit coder des URL de fichier local à l’aide d’un file:/// préfixe et de barres obliques. Par exemple, pour l’exemple Demo To Do, le chemin d’accès serait :

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

Pour copier le chemin d’accès complet avec le préfixe « file » pour un fichier local :

  1. Si vous le souhaitez, clonez le référentiel Demos afin d’avoir une copie locale. Consultez Étape 5 : Cloner le dépôt Demos dans Installation de l’extension DevTools pour Visual Studio Code.

  2. Dans Microsoft Edge, appuyez sur Ctrl+O pour ouvrir un fichier. Ouvrez un fichier local .html , tel que le fichier Demos/demo-to-do/index.htmlcloné localement :

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

    La barre d’adresse n’affiche pas initialement le file:/// préfixe, mais commence par la lettre de lecteur :

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

    Barre d’adresse de Microsoft Edge masquant initialement le préfixe file:///

  3. Cliquez sur la barre d’adresses, puis appuyez sur la touche Accueil ou appuyez sur Ctrl+A pour sélectionner le chemin d’accès entier.

    Barre d’adresse de Microsoft Edge affichant désormais le préfixe file:///

    Le chemin du fichier entier, y compris file:/// , est copié dans la mémoire tampon du Presse-papiers. Vous pouvez donc coller le chemin d’accès complet, y compris le file:/// préfixe :

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

Voir aussi :

Exemple de navigation vers une URL de fichier

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

Chargement de contenu local en accédant à une chaîne HTML

Une autre méthode pour charger du contenu local est la NavigateToString méthode . Cette approche charge le contenu dans WebView2 directement à partir d’une chaîne. Cela peut être utile si vous allez empaqueter le contenu via le code de l’application ou si vous souhaitez créer dynamiquement le contenu.

Un autre scénario où la navigation vers une chaîne peut être utile est si vous souhaitez charger du contenu qui n’est pas accessible via une URL. Par exemple, si vous avez une représentation en mémoire d’un document HTML, vous pouvez utiliser la NavigateToString méthode pour charger ce contenu dans le contrôle WebView2. Cela peut être utile si vous souhaitez éviter d’avoir à écrire le contenu dans un fichier ou un serveur avant de le charger dans le contrôle.

Considérations relatives au chargement de contenu local en accédant à une chaîne HTML

La chaîne de contenu HTML transmise à la NavigateToString méthode a une taille limite de 2 Mo. Cette limite de taille peut être facile à dépasser lorsque la chaîne inclut des ressources supplémentaires insérées. Si cette limite de taille est dépassée, une erreur est retournée : « La valeur n’entre pas dans la plage attendue ».

API DOM basées sur l’origine

Un document chargé à l’aide de la NavigateToString méthode a son emplacement défini sur about:blank et son origine a la valeur null. Cela signifie que les API web qui dépendent d’une origine définie, comme localStorage ou indexedDB, ne peuvent pas être utilisées.

API DOM nécessitant un contexte sécurisé

Certaines API web sont limitées aux URL HTTPS sécurisées uniquement et ne sont pas disponibles pour les documents chargés via la NavigateToString méthode, car leur emplacement est défini sur about:blank. Cela inclut les API telles que navigator.mediaDevices.getUserMedia() pour acquérir de la vidéo ou du son, navigator.geolocation.getCurrentPosition() pour accéder à l’emplacement de l’appareil ou Notification.requestPermission() pour demander à l’utilisateur l’autorisation d’afficher des notifications. Pour plus d’informations, consultez Sécuriser les contextes sur MDN.

Contenu dynamique

Lorsque vous chargez du contenu local via la NavigateToString méthode , vous fournissez directement le contenu en tant que paramètre à la méthode . Cela signifie que vous contrôlez le contenu au moment de l’exécution et que vous pouvez le générer dynamiquement si nécessaire.

Ressources web supplémentaires

Le chargement de contenu local à l’aide de la NavigateToString méthode ne permet pas au document obtenu de référencer des ressources web supplémentaires telles que des fichiers CSS, d’image ou de script. La méthode vous permet uniquement de spécifier le contenu de chaîne du document HTML. Pour référencer des ressources web supplémentaires à partir de votre document HTML, utilisez l’une des autres approches décrites dans cet article ou représentez ces ressources web supplémentaires inline dans le document HTML.

Ressources web supplémentaires résolues dans le processus WebView2

NavigateToString ne prend pas en charge les ressources web supplémentaires, comme mentionné ci-dessus.

API pour le chargement de contenu local en accédant à une chaîne HTML

Exemple de représentation sous forme de chaîne d’une page web

Voici la représentation sous forme de chaîne de la page web Demo To Do . La liste ci-dessous a ajouté un habillage de ligne pour plus de lisibilité. Dans la pratique, ces lignes sont concaténées en une seule ligne longue :

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

Pour obtenir la chaîne ci-dessus :

  1. Accédez à Demo To Do.

  2. Cliquez avec le bouton droit sur la page web, puis sélectionnez Inspecter pour ouvrir DevTools.

  3. Dans la console de DevTools, entrez : document.body.parentElement.outerHTML. La console génère une représentation sous forme de chaîne de la page web :

    Représentation sous forme de chaîne de la page web Demo To Do

Exemple de navigation vers une chaîne HTML

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

Chargement de contenu local à l’aide du mappage de nom d’hôte virtuel

Une autre façon de charger du contenu local dans un contrôle WebView2 consiste à utiliser le mappage de nom d’hôte virtuel. Cela implique de mapper un nom de domaine local à un dossier local, de sorte que lorsque le contrôle WebView2 tente de charger une ressource pour ce domaine, il charge le contenu à partir de l’emplacement de dossier local spécifié à la place. L’origine du document sera également le nom d’hôte virtuel.

Cette approche vous permet de spécifier l’accès cross-origin à l’aide de l’énumération CoreWebView2HostResourceAccessKind .

En raison d’une limitation actuelle, le chargement des fichiers multimédias accessibles à l’aide d’un nom d’hôte virtuel peut être lent.

Considérations relatives au chargement de contenu local à l’aide du mappage de nom d’hôte virtuel

API DOM basées sur l’origine

Le contenu local chargé via le mappage de nom d’hôte virtuel génère un document qui a une URL HTTP ou HTTPS et une origine correspondante. Cela signifie que les API web qui nécessitent une origine telle que localStorage ou indexedDB fonctionneront, et que d’autres documents qui appartiennent à la même origine pourront utiliser les données stockées. Pour plus d’informations, consultez Stratégie de même origine sur MDN.

API DOM nécessitant un contexte sécurisé

Certaines API web sont limitées aux URL HTTPS sécurisées uniquement. L’utilisation du mappage de nom d’hôte virtuel fournit une URL HTTPS pour votre contenu local. Cela signifie que des API telles que navigator.mediaDevices.getUserMedia() l’acquisition de vidéos ou de sons, navigator.geolocation.getCurrentPosition() l’accès à l’emplacement de l’appareil ou Notification.requestPermission() la demande d’autorisation de l’utilisateur pour afficher des notifications sont disponibles. Pour plus d’informations, consultez Sécuriser les contextes sur MDN.

Contenu dynamique

Lorsque vous chargez du contenu local via un mappage de nom d’hôte virtuel, vous mappez un nom d’hôte virtuel à un dossier local qui contient des fichiers statiques sur le disque. Cela signifie qu’il n’est pas possible de modifier dynamiquement ce contenu local. Cela diffère du chargement de documents à partir d’un serveur web, où chaque réponse peut être générée dynamiquement.

Ressources web supplémentaires

Le contenu local chargé via le mappage de nom d’hôte virtuel a une URL HTTP ou HTTPS qui prend en charge la résolution d’URL relative. Cela signifie que le document chargé peut avoir des références à des ressources web supplémentaires telles que des fichiers CSS, de script ou d’image, qui sont également servis via le mappage de nom d’hôte virtuel.

Ressources web supplémentaires résolues dans le processus WebView2

Les URL de nom d’hôte virtuel sont résolues dans les processus WebView2. Il s’agit d’une option plus rapide que WebResourceRequested, qui se résout dans le thread d’interface utilisateur du processus de l’application hôte.

API pour le chargement de contenu local à l’aide du mappage de nom d’hôte virtuel

Exemple de mappage de nom d’hôte virtuel

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

Chargement du contenu local en gérant l’événement WebResourceRequested

Une autre façon d’héberger du contenu local dans un contrôle WebView2 consiste à s’appuyer sur l’événement WebResourceRequested . Cet événement est déclenché lorsque le contrôle tente de charger une ressource. Vous pouvez utiliser cet événement pour intercepter la requête et fournir le contenu local, comme décrit dans Gestion personnalisée des demandes réseau.

WebResourceRequested vous permet de personnaliser le comportement du contenu local par demande. Cela signifie que vous pouvez décider des demandes à intercepter et de fournir votre propre contenu, ainsi que celles pour lesquelles le contrôle WebView2 doit s’exécuter normalement. Toutefois, la personnalisation du comportement nécessite davantage de code, comme le mappage de l’hôte virtuel, et nécessite une connaissance du protocole HTTP pour pouvoir construire une réponse appropriée.

Du point de vue de WebView2, la ressource est passée par le réseau, et WebView2 respecte les en-têtes définis par l’application dans le cadre de la réponse. L’utilisation de l’événement WebResourceRequested est également plus lente que d’autres approches, en raison de la communication et du traitement interprocessus nécessaires pour chaque requête.

Inscription de schéma personnalisé

Si vous souhaitez utiliser un schéma personnalisé pour effectuer la demande de ressource web qui génère l’événement WebResourceRequested , consultez Inscription de schéma personnalisé dans Vue d’ensemble des fonctionnalités et API WebView2.

Considérations relatives au chargement du contenu local en gérant l’événement WebResourceRequested

API DOM basées sur l’origine

Le contenu local chargé via WebResourceRequested aboutit à un document qui a une URL HTTP ou HTTPS et une origine correspondante. Cela signifie que les API web qui nécessitent une origine telle que localStorage ou indexedDB fonctionneront, et que d’autres documents qui appartiennent à la même origine pourront utiliser les données stockées. Pour plus d’informations, consultez Stratégie de même origine sur MDN.

API DOM nécessitant un contexte sécurisé

Certaines API web sont limitées aux URL HTTPS sécurisées uniquement. L’utilisation WebResourceRequested de vous permet de remplacer les demandes de ressources web URL HTTPS par votre propre contenu local. Cela signifie que des API telles que navigator.mediaDevices.getUserMedia() l’acquisition de vidéos ou de sons, navigator.geolocation.getCurrentPosition() l’accès à l’emplacement de l’appareil ou Notification.requestPermission() la demande d’autorisation de l’utilisateur pour afficher des notifications sont disponibles. Pour plus d’informations, consultez Sécuriser les contextes sur MDN.

Contenu dynamique

Lorsque vous chargez du contenu local via WebResourceRequested, vous spécifiez le contenu local à charger dans votre gestionnaire d’événements. Cela signifie que vous contrôlez le contenu au moment de l’exécution et que vous pouvez le générer dynamiquement si nécessaire.

Ressources web supplémentaires

WebResourceRequested modifie le contenu chargé via des URL HTTP ou HTTPS, qui prennent en charge la résolution d’URL relative. Cela signifie que le document obtenu peut avoir des références à des ressources web supplémentaires telles que des fichiers CSS, de script ou d’image qui sont également servis via WebResourceRequested.

Ressources web supplémentaires résolues dans le processus WebView2

Lors du chargement de contenu via une URL de fichier ou un mappage de nom d’hôte virtuel, la résolution se produit dans les processus WebView2. Toutefois, l’événement WebResourceRequested est déclenché sur le thread d’interface utilisateur WebView2 de votre processus d’application hôte, ce qui peut ralentir le chargement du document résultant.

  1. WebView2 suspend d’abord le chargement de la page web afin d’attendre que l’événement soit envoyé à votre processus d’application hôte.
  2. WebView2 attend ensuite que votre thread d’interface utilisateur soit disponible.
  3. WebView2 attend ensuite que le code de votre application gère l’événement.

Cela peut prendre un certain temps. Veillez à limiter les appels aux AddWebResourceRequestedFilter seules ressources web qui doivent déclencher l’événement WebResourceRequested .

API pour le chargement de contenu local en gérant l’événement WebResourceRequested

Exemple de gestion de l’événement 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", "");
    }
};

Voir aussi