Hosten einer Blazor-Webanwendung in einer .NET MAUI-App mit BlazorWebView

Die .NET Multiplattform App UI (.NET MAUI) BlazorWebView ist ein Steuerelement, das es Ihnen ermöglicht, eine Blazor-Web-App in Ihrer .NET MAUI-App zu hosten. Diese als Blazor-Hybrid-Apps bezeichneten Apps ermöglichen die Integration einer Blazor-Webanwendung mit Plattformfunktionen und UI-Steuerungen. Das BlazorWebView-Steuerelement kann zu jeder Seite einer .NET-MAUI-App hinzugefügt werden und auf das Root-Element der Blazor-App verweisen. Die Razor-Komponenten laufen nativ im .NET-Prozess und geben die Web-UI an ein eingebettetes Web-View-Control weiter. In .NET MAUI können Blazor Hybrid-Apps auf allen von .NET MAUI unterstützten Plattformen laufen.

BlazorWebView definiert die folgenden Eigenschaften:

  • HostPage, vom Typ string?, der die Root-Seite der Blazor-Web-App definiert.
  • RootComponents, vom Typ RootComponentsCollection, der die Sammlung von Stammkomponenten angibt, die dem Steuerelement hinzugefügt werden können.
  • StartPath, vom Typ string, der den Pfad für die anfängliche Navigation innerhalb des Blazor-Navigationskontexts definiert, wenn die Blazor-Komponente fertig geladen ist.

Die RootComponent-Klasse definiert die folgenden Eigenschaften:

  • Selector, vom Typ string?, der die CSS-Selektorzeichenfolge definiert, die angibt, wo im Dokument die Komponente platziert werden soll.
  • ComponentType, vom Typ Type?, der den Typ der Stammkomponente definiert.
  • Parameters vom Typ IDictionary<string, object?>?, der ein optionales Verzeichnis von Parametern darstellt, die an die Stammkomponente übergeben werden.

Darüber hinaus werden in BlazorWebView die folgenden Ereignisse definiert:

  • BlazorWebViewInitializing, mit einem begleitenden BlazorWebViewInitializingEventArgs-Objekt, das vor der BlazorWebView-Initialisierung ausgelöst wird. Dieses Ereignis ermöglicht die Anpassung der BlazorWebView-Konfiguration.
  • BlazorWebViewInitialized, mit einem begleitenden BlazorWebViewInitializedEventArgs-Objekt, das ausgelöst wird, nachdem das BlazorWebView initialisiert wurde, aber bevor eine Komponente gerendert wurde. Dieses Ereignis ermöglicht das Abrufen der plattformspezifischen Web-View-Instanz.
  • UrlLoading wird zusammen mit einem UrlLoadingEventArgs-Objekt ausgelöst, wenn innerhalb eines BlazorWebView ein Hyperlink angeklickt wird. Dieses Ereignis ermöglicht die Anpassung, ob ein Hyperlink in der BlazorWebView, in einer externen App geöffnet wird oder ob der URL-Ladeversuch abgebrochen wird.

Vorhandene Razor-Komponenten können in einer .NET MAUI Blazor-App verwendet werden, indem der Code in die App verschoben wird oder indem auf eine vorhandene Klassenbibliothek oder ein Paket verwiesen wird, das die Komponente enthält. Weitere Informationen finden Sie unter Wiederverwendung von Razor-Komponenten in ASP.NET Core Blazor Hybrid.

Browser-Entwickler-Tools können verwendet werden, um .NET MAUI Blazor-Apps zu inspizieren. Weitere Informationen finden Sie unter Verwenden von Browser-Entwickler-Tools mit ASP.NET Core Blazor Hybrid.

Hinweis

Während Visual Studio alle erforderlichen Werkzeuge für die Entwicklung von .NET MAUI Blazor-Apps installiert, müssen Endbenutzer*innen von .NET MAUI Blazor-Apps auf Windows die WebView2-Laufzeitumgebung installieren.

Warnung

Unter Windows können Apps, die WebView2-basierte Steuerelemente verwenden und im Program Files-Verzeichnis installiert sind, möglicherweise Inhalte nicht ordnungsgemäß rendern. Dies liegt daran, dass WebView2 versucht, seine Cache- und Benutzerdatendateien in das Installationsverzeichnis der App zu schreiben, das eingeschränkte Schreibberechtigungen enthält Program Files. Um dieses Problem zu beheben, legen Sie die WEBVIEW2_USER_DATA_FOLDER Umgebungsvariable fest, bevor ein WebView-Steuerelement initialisiert wird:

#if WINDOWS
var userDataFolder = Path.Combine(FileSystem.AppDataDirectory, "WebView2");
Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", userDataFolder);
#endif

Platzieren Sie diesen Code in Ihrem App.xaml.cs Konstruktor oder Platforms\Windows\App.xaml.cs bevor ein WebView-Steuerelement erstellt wird. Dadurch verwendet WebView2 einen schreibbaren Speicherort im AppData-Verzeichnis des Benutzers anstatt des eingeschränkten Speicherorts 'Program Files'.

Weitere Informationen zu Blazor-Hybrid-Apps finden Sie unter ASP.NET Core Blazor Hybrid.

Browser-Engines

WebView verwendet verschiedene Browsermodule auf jeder Plattform zum Rendern von Webinhalten:

  • Windows: Verwendet WebView2, das auf dem Microsoft Edge (Chromium)-Browsermodul basiert. Dies bietet Unterstützung für moderne Webstandards und ein konsistentes Verhalten mit dem Edge-Browser.
  • Android: Verwendet android.webkit.WebView, die auf dem Chromium-Browsermodul basiert. Die spezifische Version hängt von der Android WebView-Systemkomponente ab, die auf dem Gerät installiert ist.
  • iOS und Mac Catalyst: Verwendet WKWebView, die auf dem Safari WebKit-Browsermodul basiert. Dies ist dasselbe Modul, das vom Safari-Browser unter iOS und macOS verwendet wird.

Diese plattformspezifischen Implementierungen bedeuten, dass Webinhalte zwischen Plattformen unterschiedlich gerendert werden können, und einige plattformspezifische Web-APIs sind möglicherweise nur auf bestimmten Plattformen verfügbar. Testen Sie beim Entwickeln plattformübergreifender Apps Ihre Webinhalte auf allen Zielplattformen, um ein konsistentes Verhalten sicherzustellen.

Erstellen Sie eine .NET MAUI Blazor-App

Eine .NET MAUI Blazor App kann in Visual Studio mit der .NET MAUI Blazor App-Vorlage erstellt werden:

Screenshot der .NET MAUI Blazor-App-Projektvorlage.

Diese Projektvorlage erstellt eine .NET MAUI Blazor-App, die auf mehrere Plattformen wie Android, iOS, macOS und Windows ausgerichtet ist und bereitgestellt werden kann. Eine schrittweise Anleitung zum Erstellen einer .NET MAUI Blazor-App finden Sie unter Erstellen einer .NET MAUI Blazor-App.

Das BlazorWebView, das von der Projektvorlage erstellt wird, ist in MainPage.xaml definiert und verweist auf das Root-Verzeichnis der Blazor-App.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:BlazorWebViewDemo"
             x:Class="BlazorWebViewDemo.MainPage"
             BackgroundColor="{DynamicResource PageBackgroundColor}">

    <BlazorWebView HostPage="wwwroot/index.html">
        <BlazorWebView.RootComponents>
            <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
        </BlazorWebView.RootComponents>
    </BlazorWebView>

</ContentPage>

Die Stamm-Razor-Komponente für die App befindet sich in Main.razor, die Razor zu einem Typ namens Main im Stamm-Namenspace der App kompiliert. Die restlichen Razor-Komponenten befinden sich in den Projektordnern Pages und Shared und sind identisch mit den Komponenten, die in der Standard-Blazor-Webvorlage verwendet werden. Statische Web-Assets für die App befinden sich im Ordner wwwroot.

Hinzufügen einer BlazorWebView zu einer bestehenden App

Das Verfahren zum Hinzufügen eines BlazorWebView zu einer bestehenden .NET MAUI-App ist wie folgt:

  1. Fügen Sie das Razor SDK, Microsoft.NET.Sdk.Razor zu Ihrem Projekt hinzu, indem Sie die erste Zeile der CSPROJ-Projektdatei bearbeiten:

    <Project Sdk="Microsoft.NET.Sdk.Razor">
    

    Das Razor SDK wird benötigt, um Projekte zu erstellen und zu verpacken, die Razor-Dateien für Blazor-Projekte enthalten.

  2. Fügen Sie die Stammkomponente Razor für die App zum Projekt hinzu.

  3. Fügen Sie Ihre Razor-Komponenten zu den Projektordnern mit den Namen Pages und Shared hinzu.

  4. Fügen Sie Ihre statischen Web-Assets in einen Projektordner namens wwwroot ein.

  5. Fügen Sie alle optionalen _Imports.razor Dateien zu Ihrem Projekt hinzu.

  6. Fügen Sie ein BlazorWebView zu einer Seite in Ihrer .NET MAUI-App hinzu und verweisen Sie auf das Hauptverzeichnis der Blazor-App.

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:local="clr-namespace:MyBlazorApp"
                 x:Class="MyBlazorApp.MainPage">
    
        <BlazorWebView HostPage="wwwroot/index.html">
            <BlazorWebView.RootComponents>
                <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
            </BlazorWebView.RootComponents>
        </BlazorWebView>
    
    </ContentPage>
    
  7. Ändern Sie die CreateMauiApp-Methode Ihrer MauiProgram-Klasse, um das BlazorWebView-Steuerelement für die Verwendung in Ihrer App zu registrieren. Rufen Sie dazu im IServiceCollection-Objekt die AddMauiBlazorWebView-Methode auf, um Komponenten-Webansichtsdienste zur Dienstesammlung hinzuzufügen:

    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                });
    
            builder.Services.AddMauiBlazorWebView();
    #if DEBUG
            builder.Services.AddBlazorWebViewDeveloperTools();
    #endif
            // Register any app services on the IServiceCollection object
            // e.g. builder.Services.AddSingleton<WeatherForecastService>();
    
            return builder.Build();
        }
    }
    

    Dieser Code ermöglicht außerdem Entwicklertools für die zugrunde liegenden WebView-Steuerelemente, wenn die App in der Debugkonfiguration ausgeführt wird.

Zugreifen auf bereichsbezogene Dienste über die native Benutzeroberfläche

BlazorWebView verfügt über eine TryDispatchAsync-Methode, die einen angegebenen Action<ServiceProvider> asynchron aufrufen und die in Razor-Komponenten verfügbaren Scoped Services übergeben kann. Auf diese Weise kann Code über die native Benutzeroberfläche auf bereichsbezogene Dienste zugreifen, z. B. NavigationManager:

private async void OnMyMauiButtonClicked(object sender, EventArgs e)
{
    var wasDispatchCalled = await blazorWebView.TryDispatchAsync(sp =>
    {
        var navMan = sp.GetRequiredService<NavigationManager>();
        navMan.CallSomeNavigationApi(...);
    });

    if (!wasDispatchCalled)
    {
        // Consider what to do if it the dispatch fails - that's up to your app to decide.
    }
}

Diagnostizieren von Problemen

BlazorWebView verfügt über eine integrierte Protokollierung, die Ihnen bei der Diagnose von Problemen in Ihrer Blazor Hybrid-App helfen kann. Es gibt zwei Schritte, um diese Protokollierung zu aktivieren:

  1. Aktivieren Sie BlazorWebView und verwandte Komponenten zum Protokollieren von Diagnoseinformationen.
  2. Konfigurieren Sie einen Logger, der die Protokollausgabe an einen Ort schreibt, an dem Sie sie einsehen können.

Weitere Informationen zur Protokollierung finden Sie unter Protokollierung in C# und .NET.

BlazorWebView-Protokollierung aktivieren

Die gesamte Konfiguration der Protokollierung kann als Teil der Dienstregistrierung im System für die Injektion von Abhängigkeiten durchgeführt werden. Um die maximale Protokollierung für BlazorWebView und verwandte Komponenten unter dem Microsoft.AspNetCore.Components.WebView-Namenspace zu aktivieren, fügen Sie den folgenden Code dort ein, wo die Dienste Ihrer App registriert sind:

services.AddLogging(logging =>
{
    logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
});

Um die maximale Protokollierung für jede Komponente zu aktivieren, die Microsoft.Extensions.Logging verwendet, könnten Sie alternativ folgenden Code verwenden:

services.AddLogging(logging =>
{
    logging.SetMinimumLevel(LogLevel.Trace);
});

Konfigurieren der Protokollierungsausgabe und Anzeigen der Protokollausgabe

Nachdem Sie die Komponenten so konfiguriert haben, dass sie Protokollinformationen schreiben, müssen Sie konfigurieren, wohin die Logger die Protokolle schreiben sollen, und dann die Protokollausgabe anzeigen.

Die Debug Logging Provider schreiben die Ausgabe mit Debug-Anweisungen, und die Ausgabe kann in Visual Studio angezeigt werden.

Um den Debug Logging Provider zu konfigurieren, fügen Sie zunächst in Ihrem Projekt einen Verweis auf das Microsoft.Extensions.Logging.Debug NuGet-Paket hinzu. Registrieren Sie dann den Anbieter innerhalb des Aufrufs von AddLogging, den Sie im vorherigen Schritt hinzugefügt haben, indem Sie die Erweiterungsmethode AddDebug aufrufen:

services.AddLogging(logging =>
{
    logging.AddFilter("Microsoft.AspNetCore.Components.WebView", LogLevel.Trace);
    logging.AddDebug();
});

Wenn Sie die App von Visual Studio aus ausführen (mit aktiviertem Debugging), können Sie die Debug-Ausgabe im Fenster Ausgabe von Visual Studio anzeigen.

Wiedergeben von Inlinevideos unter iOS

Zum Abspielen von Inline-Videos in einer Blazor-Hybrid-App auf iOS in einem BlazorWebView sollten Sie:

  • Setzen Sie die UrlLoadingStrategy-Eigenschaft auf OpenInWebView. Dies kann im Ereignishandler für das UrlLoading-Ereignis erreicht werden:

    private void BlazorUrlLoading(object? sender, UrlLoadingEventArgs e)
    {
    #if IOS
        e.UrlLoadingStrategy = UrlLoadingStrategy.OpenInWebView;
    #endif
    }
    
  • Stellen Sie sicher, dass die AllowsInlineMediaPlayback-Eigenschaft in einem Configuration-Objekt auf true festgelegt ist. Dies kann im Ereignishandler für das BlazorWebViewInitializing-Ereignis erreicht werden:

    private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e)
    {
    #if IOS
        e.Configuration.AllowsInlineMediaPlayback = true;
    #endif
    }
    

Behebung von Deadlocks in Android

Löst standardmäßig BlazorWebView die asynchrone Entsorgung der zugrunde liegenden WebViewManager. Dies reduziert das Potenzial für das Auftreten von Deadlocks auf Android.

Warnung

Dieses "Fire-and-Forget"-Standardverhalten bedeutet, dass die Entsorgung vor dem Löschen aller Objekte zurückgegeben werden kann, was Verhaltensänderungen in Ihrer App verursachen kann. Die verworfenen Elemente sind teilweise die eigenen internen Typen von Blazor, aber auch App-definierte Typen wie bereichsbezogene Dienste, die innerhalb des Teils BlazorWebView Ihrer App verwendet werden.

Um dieses Verhalten zu deaktivieren, sollten Sie Ihre App so konfigurieren, dass sie beim Entsorgen mit einem AppContext Schalter in Ihrer MauiProgram Klasse blockiert wird.

static MauiProgram()
{
    AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", false);
}

Wenn Ihre App so konfiguriert ist, dass das Entsorgen über diesen Switch blockiert wird, BlazorWebView wird eine asynchrone-über-synchrone Entsorgung durchgeführt. Dies bedeutet, dass der Thread blockiert wird, bis die asynchrone Entsorgung abgeschlossen ist. Dies kann jedoch zu Deadlocks führen, wenn bei der Entsorgung Code für denselben Thread ausgeführt werden muss (da der Thread während des Wartens blockiert wird).

Hosten von Inhalten mithilfe des Legacyverhaltens

Das Standardverhalten für das Hosten von Inhalten hat sich in einem BlazorWebView auf 0.0.0.1geändert. Die interne 0.0.0.0-Adresse, die zum Hosten von Inhalten verwendet wurde, funktioniert nicht mehr und führt dazu, das BlazorWebView keine Inhalte lädt und ein leeres Rechteck angezeigt wird.

Um sich für die Verwendung der 0.0.0.0 Adresse zu entscheiden, fügen Sie Ihrer MauiProgram Klasse den folgenden Code hinzu:

static MauiProgram()
{
    // Set this switch to use the LEGACY behavior of always using 0.0.0.0 to host BlazorWebView
    AppContext.SetSwitch("BlazorWebView.AppHostAddressAlways0000", true);
}

Abfangen von Webanforderungen

BlazorWebView kann Webanforderungen abfangen und beantworten, die in der eingebetteten Webansicht initiiert wurden. Dies ermöglicht Szenarien wie das Ändern von Headern, das Umleiten von Anforderungen oder das Bereitstellen lokaler Antworten.

Um Webanforderungen abzufangen, behandeln Sie das WebResourceRequested Ereignis. Legen Sie im Ereignishandler Handled auf true fest, und geben Sie eine Antwort über SetResponse(statusCode, statusDescription, contentType, streamOrTaskOfStream) aus, wenn Sie die Anforderung vollständig bearbeiten möchten.

blazorWebView.WebResourceRequested += (s, e) =>
{
    // Example: short-circuit a specific API call with a local JSON response
    if (e.Uri.ToString().Contains("/api/secure", StringComparison.OrdinalIgnoreCase))
    {
        e.Handled = true;
        e.SetResponse(200, "OK", "application/json", GetLocalJsonStreamAsync());
        return;
    }

    // Example: add an auth header for a particular host and allow normal processing
    if (e.Uri.Host.Equals("api.contoso.com", StringComparison.OrdinalIgnoreCase))
    {
        e.RequestHeaders["Authorization"] = $"Bearer {GetToken()}";
        // Don't set Handled; let the request proceed
    }
};

private Task<Stream> GetLocalJsonStreamAsync()
{
    // Return a stream containing JSON (for example from an embedded asset)
    var json = Encoding.UTF8.GetBytes("{\"message\":\"Hello from local\"}");
    return Task.FromResult<Stream>(new MemoryStream(json));
}

Vorsicht

Der WebResourceRequested Rückruf muss synchron im WebView-Thread ausgeführt werden. Wenn Sie Handled = true festlegen, müssen Sie sofort SetResponse aufrufen, um eine Antwort bereitzustellen. Übergeben Sie für die asynchrone Inhaltsgenerierung eine Task<Stream> an SetResponse, sodass WebView weiterarbeiten kann, während der Datenstrom abgeschlossen wird.

Zu den gängigen Mustern gehören:

  • Einfügen oder Umschreiben von Headern für bestimmte Hosts.
  • Zurückgeben lokaler Dateien oder Speicherinhalte für Offline- oder Testszenarien.
  • Umleitung zu einem anderen URI durch Zurückgeben eines 3xx-Status mit einer Location Kopfzeile.