webView->SetVirtualHostNameToFolderMapping(L"demo", L"C:\\Github\\Demos\\demo-to-do",
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS);
webView->Navigate(L"https://demo/index.html");
通过处理 WebResourceRequested 事件加载本地内容
在 WebView2 控件中托管本地内容的另一种方法是依赖于 WebResourceRequested
事件。 当控件尝试加载资源时,会触发此事件。 可以使用此事件来截获请求并提供本地内容,如 网络请求的自定义管理中所述。
WebResourceRequested
允许你基于每个请求自定义本地内容的行为。 这意味着你可以决定要拦截哪些请求并提供你自己的内容,以及让 WebView2 控件正常处理的请求。 但是,自定义行为需要更多代码(例如虚拟主机映射),并且需要了解 HTTP 才能构造正确的响应。
从 WebView2 的角度来看,资源将通过网络提供,WebView2 将遵循应用在响应过程中设置的标头。
WebResourceRequested
使用事件的速度也比其他方法慢,因为每个请求都需要跨进程通信和处理。
自定义方案注册
如果要使用自定义方案发出生成WebResourceRequested
事件的 Web 资源请求,请参阅 WebView2 功能和 API 概述中的自定义方案注册。
通过处理 WebResourceRequested 事件加载本地内容的注意事项
基于源的 DOM API
通过 WebResourceRequested
加载的本地内容会导致文档中具有 HTTP 或 HTTPS URL 以及相应的源。 这意味着需要源(如 或indexedDB
)localStorage
的 Web API 将正常工作,属于同一源的其他文档将能够使用存储的数据。 有关详细信息,请参阅 MDN 上的 同源策略 。
需要安全上下文的 DOM API
某些 Web API 仅限于安全 HTTPS URL。 使用 WebResourceRequested
可将 HTTPS URL Web 资源请求替换为自己的本地内容。 这意味着可以使用 API,例如 navigator.mediaDevices.getUserMedia()
获取视频或声音、 navigator.geolocation.getCurrentPosition()
访问设备位置或 Notification.requestPermission()
请求用户显示通知的权限。 有关详细信息 ,请参阅保护 MDN 上的上下文。
通过 WebResourceRequested
加载本地内容时,指定要在事件处理程序中加载的本地内容。 这意味着可以在运行时控制内容,并且可以根据需要动态生成内容。
其他 Web 资源
WebResourceRequested
修改通过 HTTP 或 HTTPS URL 加载的内容,这些 URL 支持相对 URL 解析。 这意味着生成的文档可以引用其他 Web 资源,例如 CSS、脚本或图像文件,这些文件也通过 WebResourceRequested
提供。
在 WebView2 进程中解析的其他 Web 资源
通过文件 URL 或虚拟主机名映射加载内容时,解析将在 WebView2 进程中发生。 但是,该 WebResourceRequested
事件在主机应用进程的 WebView2 UI 线程上引发,这可能会导致生成的文档加载速度变慢。
WebView2 首先暂停加载网页,以便等待事件发送到主机应用进程。
然后,WebView2 等待 UI 线程可用。
然后,WebView2 等待应用代码处理事件。
这可能需要一些时间。 请确保仅对必须引发WebResourceRequested
事件的 Web 资源的调用AddWebResourceRequestedFilter
。
用于通过处理 WebResourceRequested 事件加载本地内容的 API
// 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;
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);
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", "");
public IInputStream GetInputStreamAt(ulong position)
throw new NotImplementedException();
public IOutputStream GetOutputStreamAt(ulong position)
throw new NotImplementedException();
public void Seek(ulong position)
return s_.Seek(position);
public IRandomAccessStream CloneStream()
throw new NotImplementedException();
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer,
uint count, InputStreamOptions options)
IAsyncOperationWithProgress<IBuffer, uint> result;
result = s_.ReadAsync(buffer, count, options);
// Once read is complete if no data was read, dispose the underlying
// stream.
result.Completed += new AsyncOperationWithProgressCompletedHandler<IBuffer, uint>(
delegate (IAsyncOperationWithProgress<IBuffer, uint> asyncInfo,
AsyncStatus asyncStatus)
if (asyncInfo.GetResults().Length == 0)
s_.Dispose();
catch
s_.Dispose();
throw;
return result;
public IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer)
throw new NotImplementedException();
public IAsyncOperation<bool> FlushAsync()
throw new NotImplementedException();
public void Dispose()
throw new NotImplementedException();
private IRandomAccessStream s_;
WebView2.CoreWebView2.AddWebResourceRequestedFilter("https://demo/*",
CoreWebView2WebResourceContext.All);
WebView2.CoreWebView2.WebResourceRequested += async delegate (CoreWebView2 sender,
CoreWebView2WebResourceRequestedEventArgs args)
string filename = args.Request.Uri.Substring("https://demo/*".Length - 1);
Windows.Storage.StorageFolder storageFolder =
Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFolder demo = await storageFolder.GetFolderAsync("Demo");
Windows.Storage.StorageFile asset = await demo.GetFileAsync(filename);
ManagedStream ms = new ManagedStream(await asset.OpenReadAsync());
string headers = "";
if (filename.EndsWith(".html"))
headers = "Content-Type: text/html";
else if (filename.EndsWith(".jpg"))
headers = "Content-Type: image/jpeg";
else if (filename.EndsWith(".png"))
headers = "Content-Type: image/png";
else if (filename.EndsWith(".css"))
headers = "Content-Type: text/css";
else if (filename.EndsWith(".js"))
headers = "Content-Type: application/javascript";
args.Response = WebView2.CoreWebView2.Environment.CreateWebResourceResponse(ms,
200, "OK", headers);
catch (Exception)
args.Response = WebView2.CoreWebView2.Environment.CreateWebResourceResponse(null,
404, "Not found", "");
CHECK_FAILURE(m_webView->AddWebResourceRequestedFilter(
L"https://demo/*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL));
CHECK_FAILURE(m_webView->add_WebResourceRequested(
Callback<ICoreWebView2WebResourceRequestedEventHandler>(
this, &AppWindow::WebResourceRequestedEventHandler)
.Get(),
&m_webResourceRequestedToken));
HRESULT AppWindow::WebResourceRequestedEventHandler(
ICoreWebView2* webview, ICoreWebView2WebResourceRequestedEventArgs* args)
wil::com_ptr<ICoreWebView2WebResourceRequest> request;
wil::com_ptr<ICoreWebView2WebResourceResponse> response;
wil::com_ptr<IStream> stream;
CHECK_FAILURE(args->get_Request(&request));
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(request->get_Uri(&uri));
std::wstring assetsFilePath = L"C:\\Demo";
assetsFilePath += (uri.get() + ARRAYSIZE(L"https://demo"));
SHCreateStreamOnFileEx(
assetsFilePath.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE,
nullptr, &stream);
if (stream)
std::wstring headers;
if (assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) ==
L"html")
headers = L"Content-Type: text/html";
else if (
assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) ==
L"jpg")
headers = L"Content-Type: image/jpeg";
else if (
assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) ==
L"png")
headers = L"Content-Type: image/png";
else if (
assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) ==
L"css")
headers = L"Content-Type: text/css";
else if (
assetsFilePath.substr(assetsFilePath.find_last_of(L".") + 1) ==
L"js")
headers = L"Content-Type: application/javascript";
CHECK_FAILURE(
webViewEnvironment->CreateWebResourceResponse(
stream.get(), 200, L"OK", headers.c_str(), &response));
CHECK_FAILURE(args->put_Response(response.get()));
CHECK_FAILURE(
webViewEnvironment->CreateWebResourceResponse(
nullptr, 404, L"Not Found", L"", &response));
CHECK_FAILURE(args->put_Response(response.get()));