将 WebView2 REST 调用路由到本地 .NET 5 控制器

Routing WebView2 REST calls to local .NET 5 Controllers

我目前正在设计一个 Angular SPA 网络客户端,支持 .NET5 REST。它们都在同一个 Visual Studio 项目中,并且构建/运行良好。

我现在正在研究将其作为 windows 桌面应用程序分发的可能性。我能够让 Electron.NET 工作,但这似乎是一个迂回的解决方案(节点?!)。我也不是特别喜欢分布式应用程序中的资源visible/changeable。

这促使我调查在 WPF 中使用 WebView2(Microsoft 似乎正在对 MSTeams 进行类似的转换。)我找到了一些示例,但它们仅使用:

  1. 仅远程内容 ("www.bing.com")
  2. local content, but only img / html / etc
  3. postmessage 等使用自定义对象进行通信。

None这些就是我想要的。嗯,这不完全正确。我需要 #2 来加载 Angular SPA,但是 当 WebView2 托管的 Angular 调用 HttpClient 时,我想在主机应用程序中拦截该请求并将其路由到我的REST 控制器。这将使我能够保持几乎所有代码的完整性,并可能发布一个更小、更混淆的 exe。

这可能吗?明显的?我的愿望有根本性的缺陷吗? (不会是第一次)

Chromium.AspNetCore.Bridge 提供了解决问题的方法。它使用 owin 在内存中托管服务器端代码,并提供一个 RequestInterceptor 来将所有请求干净地中继到“服务器”代码。

上面的 link 有工作示例,但简要说明:

App.xaml.cs:

private IWebHost _host;
private AppFunc _appFunc;

public AppFunc AppFunc
{
    get { return _appFunc; }
}

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");

    _ = Task.Run(async () =>
      {
          var builder = new WebHostBuilder();

          builder.ConfigureServices(services =>
          {
              var server = new OwinServer();
              server.UseOwin(appFunc =>
              {
                  _appFunc = appFunc;
              });

              services.AddSingleton<IServer>(server);
          });

          _host = builder
              .UseStartup<Startup>()
              .UseContentRoot(Directory.GetCurrentDirectory())
              .Build();

          await _host.RunAsync();
      });            
}

MainWindow.xaml.cs

private AppFunc _appFunc;
public MainWindow()
{
    InitializeComponent();

    Browser.CoreWebView2InitializationCompleted += Browser_CoreWebView2InitializationCompleted;
}

private void Browser_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        _appFunc = ((App)Application.Current).AppFunc;
        Browser.CoreWebView2.WebResourceRequested += BrowserWebResourceRequestedAsync;
        Browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All);
    }
}

private async void BrowserWebResourceRequestedAsync(object sender, CoreWebView2WebResourceRequestedEventArgs e)
{
    var deferral = e.GetDeferral();

    var request = new ResourceRequest(e.Request.Uri, e.Request.Method, e.Request.Headers, e.Request.Content);

    var response = await RequestInterceptor.ProcessRequest(_appFunc, request);

    var coreWebView2 = (CoreWebView2)sender;

    e.Response = coreWebView2.Environment.CreateWebResourceResponse(response.Stream, response.StatusCode, response.ReasonPhrase, response.GetHeaderString());

    deferral.Complete();
}