c#使用webView2 访问本地静态html资源跨域Cors问题 (附带代理服务helper帮助类)
背景
在浏览器中访问本地静态资源html网页时,可能会遇到跨域问题如图。
是因为浏览器默认启用了同源策略,即只允许加载与当前网页具有相同源(协议、域名和端口)的内容。
WebView2默认情况下启用了浏览器的同源策略,即只允许加载与主机相同源的内容。所以如果我们把静态资源发布到iis或者通过node进行启动就可以看到不跨域了。
解决方案
-
使用CORS(Cross-Origin Resource Sharing):如果你有控制服务器端,可以在服务器端配置CORS来允许跨域请求。在服务器端的响应头中添加相关的CORS头部信息,例如允许访问的域名、请求方法等,以允许JavaScript跨域访问。
- 使用WebView2的
AddWebResourceRequestedFilter
方法:通过添加Web资源请求过滤器,你可以拦截WebView2控件中加载的资源请求,并进行处理。在拦截到JavaScript文件请求时,修改响应头部信息,添加Access-Control-Allow-Origin
头部来解决跨域问题。 - 使用代理服务器:你可以在本地启动一个代理服务器,将WebView2控件的请求转发到代理服务器上,然后代理服务器再将请求发送到原始服务器并返回响应。在代理服务器上你可以设置合适的CORS头部信息来解决跨域问题。
思路
-
首先,确保你已经安装了
Microsoft.Web.WebView2
。你可以在Visual Studio的NuGet包管理器中搜索并安装此包。 - 然后通过HttpListener进行文件夹的静态资源进行代理发布
- 然后通过webview2进行导航访问即可我们会发现跨域问题已经解决
代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WinApp.View { public partial class Cors : Form { // 创建HttpListener对象并指定绑定的端口 HttpListener _listener; string _folderPath; string _rootDirectory; public Cors() { InitializeComponent(); // 初始化 InitializeAsync(); } private async void InitializeAsync() { // 获取本地静态资源的路径 _rootDirectory = AppDomain.CurrentDomain.BaseDirectory + "offline-exam-player"; //设置本地离线播放器为代理服务 _rootDirectory = @"C:\Users\admin\Documents\WeChat Files\wxid_1ofgk575ybpt22\FileStorage\File\2024-02\ng-alain8\ng-alain8/"; _folderPath = @"C:\Users\admin\Documents\WeChat Files\wxid_1ofgk575ybpt22\FileStorage\File\2024-02\ng-alain8\ng-alain8/index.html"; _listener = new HttpListener(); // 设置代理服务器的监听地址和端口号 _listener.Prefixes.Add("http://localhost:8080/"); _listener.Start(); // 启动代理服务器 Task.Run(() => { // 启动代理服务器 ProcessRequests(); }); // 停止代理服务器(这里演示就不停止了) //server.Stop(); } private void ProcessRequests() { try { while (_listener.IsListening) { HttpListenerContext context = _listener.GetContext(); string requestPath = context.Request.Url.AbsolutePath; string filePath = _rootDirectory + requestPath; // Serve the requested file if it exists if (System.IO.File.Exists(filePath)) { string extension = System.IO.Path.GetExtension(filePath); string contentType; switch (extension) { case ".html": contentType = "text/html"; break; case ".js": contentType = "application/javascript"; break; case ".less": case ".css": contentType = "text/css"; break; case ".svg": contentType = "image/svg+xml"; break; default: contentType = "application/octet-stream"; break; } context.Response.ContentType = contentType; //context.Response.ContentType = "text/html"; byte[] responseBuffer = System.IO.File.ReadAllBytes(filePath); context.Response.OutputStream.Write(responseBuffer, 0, responseBuffer.Length); context.Response.Close(); } else { // Return a 404 response if the file does not exist context.Response.StatusCode = 404; context.Response.Close(); } } } catch (Exception ex) { // Handle any exceptions that may occur Console.WriteLine(ex.ToString()); } } private async void Cors_Load(object sender, EventArgs e) { //本地静态资源,直接访问会出现跨院,如果通过iis访问则不会跨域; // 确保CoreWebView2运行时已准备就绪 await webView21.EnsureCoreWebView2Async(); // 在WebView2控件中加载URL //webView21.CoreWebView2.Navigate(_folderPath); webView21.CoreWebView2.Navigate("http://localhost:8080/" + "index.html"); } } }
代理服务帮助类代码:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace WinApp.Until { public class ProxyHelper { private readonly HttpListener _listener; private readonly string _rootDirectory; /// <summary> /// 实例化 /// </summary> /// <param name="serviceIp">代理的ip地址带端口,例子:http://localhost:8080/ </param> /// <param name="folderPath">需要代理的文件夹,例子:AppDomain.CurrentDomain.BaseDirectory + "offline-exam-player" </param> public ProxyHelper(string serviceIp, string folderPath) { _rootDirectory = folderPath; _listener = new HttpListener(); _listener.Prefixes.Add(serviceIp); } public async Task Start() { _listener.Start(); await Task.Run(() => ProcessRequests()); } public void Stop() { _listener.Stop(); _listener.Close(); Console.WriteLine("Proxy server stopped."); } private void ProcessRequests() { try { while (_listener.IsListening) { HttpListenerContext context = _listener.GetContext(); string requestPath = context.Request.Url.AbsolutePath; string filePath = _rootDirectory + requestPath; // Serve the requested file if it exists if (System.IO.File.Exists(filePath)) { string extension = System.IO.Path.GetExtension(filePath); string contentType; switch (extension) { case ".html": contentType = "text/html"; break; case ".js": contentType = "application/javascript"; break; case ".less": case ".css": contentType = "text/css"; break; case ".svg": contentType = "image/svg+xml"; break; default: contentType = "application/octet-stream"; break; } context.Response.ContentType = contentType; //context.Response.ContentType = "text/html"; byte[] responseBuffer = System.IO.File.ReadAllBytes(filePath); context.Response.OutputStream.Write(responseBuffer, 0, responseBuffer.Length); context.Response.Close(); } else { // Return a 404 response if the file does not exist context.Response.StatusCode = 404; context.Response.Close(); } } } catch (Exception ex) { // Handle any exceptions that may occur Console.WriteLine(ex.ToString()); } } } }
结语
最后如果对于不多的跨域js文件,可以把js的代码内嵌到index.html页面实现。就是<script>跨域js内容</script>
从前慢,车马慢。
一生只爱一个人。