将 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 进行类似的转换。)我找到了一些示例,但它们仅使用:
- 仅远程内容 ("www.bing.com")
- local content, but only img / html / etc
- 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();
}
我目前正在设计一个 Angular SPA 网络客户端,支持 .NET5 REST。它们都在同一个 Visual Studio 项目中,并且构建/运行良好。
我现在正在研究将其作为 windows 桌面应用程序分发的可能性。我能够让 Electron.NET 工作,但这似乎是一个迂回的解决方案(节点?!)。我也不是特别喜欢分布式应用程序中的资源visible/changeable。
这促使我调查在 WPF 中使用 WebView2(Microsoft 似乎正在对 MSTeams 进行类似的转换。)我找到了一些示例,但它们仅使用:
- 仅远程内容 ("www.bing.com")
- local content, but only img / html / etc
- 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();
}