在WinForms应用程序中使用WebView2控件发送普通表单(application/x-www-form-urlencoded)的HTTP POST请求,是实现用户登录、提交数据等功能的常见需求。本文将详细介绍如何在WinForms中使用WebView2控件执行普通表单的POST请求,包括使用NavigateWithWebResourceRequest方法和通过JavaScript执行POST请求的两种主要方法。


目录

  1. 前提条件

  2. 方法一:使用 NavigateWithWebResourceRequest 执行普通表单POST请求

  3. 方法二:通过JavaScript在WebView2中执行普通表单POST请求

  4. 完整示例:在WinForms中集成WebView2并执行普通表单POST请求

  5. 注意事项

  6. 常见问题及解决方法

  7. 参考资料

  8. 总结


1. 前提条件

在开始之前,请确保已经完成以下准备工作:

  • WebView2 控件已成功添加到 WinForms 项目中:确保WebView2控件已正确集成。

  • 安装WebView2运行时:确保系统中安装了WebView2运行时。

  • 项目目标框架:建议使用 .NET Framework 4.6.2 及以上,或 .NET Core/5+/6+。


2. 方法一:使用 NavigateWithWebResourceRequest 执行普通表单POST请求

NavigateWithWebResourceRequest 方法允许构建一个完整的HTTP请求,包括请求方法(如POST)、请求头和请求正文,从而实现更灵活的导航和数据提交。对于普通表单提交,需要设置Content-Typeapplication/x-www-form-urlencoded

步骤概述

  1. 构建application/x-www-form-urlencoded的请求体,即将表单数据编码为URL编码格式。

  2. 设置请求头,包括Content-Type

  3. 创建CoreWebView2WebResourceRequest对象,配置HTTP方法、URL、请求头和请求内容。

  4. 调用NavigateWithWebResourceRequest方法,传递构建的请求对象。

  5. 处理导航完成后的响应,如获取响应状态或内容(需要进一步实现)。

示例代码

以下示例展示如何在WinForms应用程序中使用WebView2控件执行一个普通表单类型的POST请求,提交用户名和密码。

代码示例(C#)

using System;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Windows.Forms;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;

namespace WebView2FormPostExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            InitializeAsync();
        }

        private async void InitializeAsync()
        {
            try
            {
                // 初始化 WebView2
                await webView21.EnsureCoreWebView2Async(null);

                // 可选:订阅导航完成事件
                webView21.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;

                // 导航到一个空白页面,准备执行POST请求
                webView21.CoreWebView2.Navigate("about:blank");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"WebView2 初始化失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private async void btnPost_Click(object sender, EventArgs e)
        {
            try
            {
                // 确保WebView2已初始化
                if (webView21.CoreWebView2 == null)
                {
                    MessageBox.Show("WebView2尚未初始化。请稍候再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // 构建URL编码的表单数据
                var formData = new
                {
                    username = "zhangsan",
                    password = "password123"
                };
                string postData = $"username={Uri.EscapeDataString(formData.username)}&password={Uri.EscapeDataString(formData.password)}";
                byte[] dataBytes = Encoding.UTF8.GetBytes(postData);
                var stream = new MemoryStream(dataBytes);

                // 设置请求头
                string contentType = "application/x-www-form-urlencoded";

                // 创建请求
                var request = webView21.CoreWebView2.Environment.CreateWebResourceRequest(
                    "https://www.example.com/login", // 替换为目标URL
                    "POST",
                    stream,
                    $"Content-Type: {contentType}");

                // 订阅 NavigationCompleted 事件(可选)
                webView21.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;

                // 执行POST请求
                webView21.CoreWebView2.NavigateWithWebResourceRequest(request);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"执行POST请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
            if (e.IsSuccess)
            {
                MessageBox.Show("POST请求执行完成,导航成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                // 这里可以进一步处理响应内容,需要使用WebResourceRequested事件来拦截响应
            }
            else
            {
                MessageBox.Show($"导航失败,错误代码: {e.WebErrorStatus}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 拦截响应内容(方法一)
        private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
        {
            if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
            {
                var response = e.Response;
                if (response != null)
                {
                    // 读取响应内容
                    using (var stream = response.Content)
                    using (var reader = new StreamReader(stream))
                    {
                        string responseBody = await reader.ReadToEndAsync();
                        MessageBox.Show($"响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
        }
    }
}

设计器文件(Form1.Designer.cs)

确保Form1.Designer.cs包含WebView2控件和一个按钮用于触发POST请求。

namespace WebView2FormPostExample
{
    partial class Form1
    {
        private Microsoft.Web.WebView2.WinForms.WebView2 webView21;
        private System.Windows.Forms.Button btnPost;

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
            this.btnPost = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
            this.SuspendLayout();
            // 
            // webView21
            // 
            this.webView21.CreationProperties = null;
            this.webView21.DefaultBackgroundColor = System.Drawing.Color.White;
            this.webView21.Location = new System.Drawing.Point(12, 12);
            this.webView21.Name = "webView21";
            this.webView21.Size = new System.Drawing.Size(776, 400);
            this.webView21.TabIndex = 0;
            this.webView21.ZoomFactor = 1D;
            // 
            // btnPost
            // 
            this.btnPost.Location = new System.Drawing.Point(12, 420);
            this.btnPost.Name = "btnPost";
            this.btnPost.Size = new System.Drawing.Size(150, 30);
            this.btnPost.TabIndex = 1;
            this.btnPost.Text = "执行普通表单POST";
            this.btnPost.UseVisualStyleBackColor = true;
            this.btnPost.Click += new System.EventHandler(this.btnPost_Click);
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(800, 462);
            this.Controls.Add(this.btnPost);
            this.Controls.Add(this.webView21);
            this.Name = "Form1";
            this.Text = "WebView2 执行普通表单POST请求示例";
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
            this.ResumeLayout(false);
        }
    }
}

代码解释

  1. 构建URL编码的表单数据

    var formData = new
    {
        username = "zhangsan",
        password = "password123"
    };
    string postData = $"username={Uri.EscapeDataString(formData.username)}&password={Uri.EscapeDataString(formData.password)}";
    byte[] dataBytes = Encoding.UTF8.GetBytes(postData);
    var stream = new MemoryStream(dataBytes);
    
    • 使用匿名类型创建表单数据。

    • 使用Uri.EscapeDataString确保数据正确URL编码。

    • 将编码后的数据转换为字节数组并存入MemoryStream

  2. 设置请求头

    string contentType = "application/x-www-form-urlencoded";
    
    • 设置Content-Typeapplication/x-www-form-urlencoded

  3. 创建并执行POST请求

    var request = webView21.CoreWebView2.Environment.CreateWebResourceRequest(
        "https://www.example.com/login", // 替换为目标URL
        "POST",
        stream,
        $"Content-Type: {contentType}");
    
    webView21.CoreWebView2.NavigateWithWebResourceRequest(request);
    
    • 使用CreateWebResourceRequest方法创建POST请求。

    • 使用NavigateWithWebResourceRequest方法执行POST请求。

  4. 处理导航完成事件

    private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
    {
        if (e.IsSuccess)
        {
            MessageBox.Show("POST请求执行完成,导航成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
            // 这里可以进一步处理响应内容,需要使用WebResourceRequested事件来拦截响应
        }
        else
        {
            MessageBox.Show($"导航失败,错误代码: {e.WebErrorStatus}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    
    • 检查导航是否成功,并根据结果向用户反馈。

  5. 拦截响应内容

    private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
    {
        if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
        {
            var response = e.Response;
            if (response != null)
            {
                // 读取响应内容
                using (var stream = response.Content)
                using (var reader = new StreamReader(stream))
                {
                    string responseBody = await reader.ReadToEndAsync();
                    MessageBox.Show($"响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
    }
    
    • 使用WebResourceRequested事件拦截特定请求的响应。

    • 读取响应内容并向用户展示。


3. 方法二:通过JavaScript在WebView2中执行普通表单POST请求

除了使用NavigateWithWebResourceRequest方法,可以通过在WebView2中执行JavaScript代码(如使用Fetch API或XMLHttpRequest)来执行普通表单POST请求。这种方法适用于需要在网页上下文中执行复杂交互或获取响应内容的场景。

步骤概述

  1. 在WebView2中注入并执行JavaScript代码,使用Fetch API发送application/x-www-form-urlencoded POST请求。

  2. 通过WebView2的WebMessageReceived事件接收从网页传回的响应数据。

  3. 处理接收到的数据,如显示在WinForms应用中或进行其他操作。

示例代码

以下示例展示如何在WinForms应用程序中使用WebView2控件,通过JavaScript执行普通表单类型的POST请求,并将响应结果传回应用程序。

代码示例(C#)

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Windows.Forms;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;

namespace WebView2FormPostExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            InitializeAsync();
        }

        private async void InitializeAsync()
        {
            try
            {
                // 初始化 WebView2
                await webView21.EnsureCoreWebView2Async(null);

                // 订阅 WebMessageReceived 事件
                webView21.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;

                // 导航到一个空白页面,准备执行JavaScript
                webView21.CoreWebView2.NavigateToString("<html><body></body></html>");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"WebView2 初始化失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private async void btnPost_Click(object sender, EventArgs e)
        {
            try
            {
                // 确保WebView2已初始化
                if (webView21.CoreWebView2 == null)
                {
                    MessageBox.Show("WebView2尚未初始化。请稍候再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // 构建JavaScript代码,使用Fetch API发送application/x-www-form-urlencoded POST请求
                string script = @"
                    (async () => {
                        try {
                            const params = new URLSearchParams();
                            params.append('username', 'zhangsan');
                            params.append('password', 'password123');

                            const response = await fetch('https://www.example.com/login', { // 替换为目标URL
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/x-www-form-urlencoded'
                                },
                                body: params.toString()
                            });

                            if (!response.ok) {
                                throw new Error('网络响应不是OK');
                            }

                            const result = await response.text(); // 根据需要选择解析方式
                            window.chrome.webview.postMessage(JSON.stringify({ success: true, data: result }));
                        } catch (error) {
                            window.chrome.webview.postMessage(JSON.stringify({ success: false, error: error.message }));
                        }
                    })();
                ";

                // 执行JavaScript代码
                await webView21.CoreWebView2.ExecuteScriptAsync(script);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"执行POST请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
            try
            {
                string message = e.TryGetWebMessageAsString();
                var response = JsonSerializer.Deserialize<Dictionary<string, object>>(message);

                if (response.ContainsKey("success") && (bool)response["success"])
                {
                    string data = response["data"].ToString();
                    textBoxResponse.Text = data;
                    MessageBox.Show("成功获取POST响应!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else if (response.ContainsKey("error"))
                {
                    string error = response["error"].ToString();
                    MessageBox.Show($"POST请求错误: {error}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"处理响应失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

设计器文件(Form1.Designer.cs)

确保Form1.Designer.cs包含WebView2控件、一个按钮用于触发POST请求和一个文本框用于显示响应内容。

namespace WebView2FormPostExample
{
    partial class Form1
    {
        private Microsoft.Web.WebView2.WinForms.WebView2 webView21;
        private System.Windows.Forms.Button btnPost;
        private System.Windows.Forms.TextBox textBoxResponse;

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
            this.btnPost = new System.Windows.Forms.Button();
            this.textBoxResponse = new System.Windows.Forms.TextBox();
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
            this.SuspendLayout();
            // 
            // webView21
            // 
            this.webView21.CreationProperties = null;
            this.webView21.DefaultBackgroundColor = System.Drawing.Color.White;
            this.webView21.Location = new System.Drawing.Point(12, 12);
            this.webView21.Name = "webView21";
            this.webView21.Size = new System.Drawing.Size(776, 300);
            this.webView21.TabIndex = 0;
            this.webView21.ZoomFactor = 1D;
            // 
            // btnPost
            // 
            this.btnPost.Location = new System.Drawing.Point(12, 318);
            this.btnPost.Name = "btnPost";
            this.btnPost.Size = new System.Drawing.Size(200, 30);
            this.btnPost.TabIndex = 1;
            this.btnPost.Text = "执行普通表单POST";
            this.btnPost.UseVisualStyleBackColor = true;
            this.btnPost.Click += new System.EventHandler(this.btnPost_Click);
            // 
            // textBoxResponse
            // 
            this.textBoxResponse.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
                                                                            | System.Windows.Forms.AnchorStyles.Left) 
                                                                            | System.Windows.Forms.AnchorStyles.Right)));
            this.textBoxResponse.Location = new System.Drawing.Point(12, 354);
            this.textBoxResponse.Multiline = true;
            this.textBoxResponse.Name = "textBoxResponse";
            this.textBoxResponse.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxResponse.Size = new System.Drawing.Size(776, 84);
            this.textBoxResponse.TabIndex = 2;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.textBoxResponse);
            this.Controls.Add(this.btnPost);
            this.Controls.Add(this.webView21);
            this.Name = "Form1";
            this.Text = "WebView2 执行普通表单POST请求示例";
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }
    }
}

代码解释

  1. 构建URL编码的表单数据

    var formData = new
    {
        username = "zhangsan",
        password = "password123"
    };
    string postData = $"username={Uri.EscapeDataString(formData.username)}&password={Uri.EscapeDataString(formData.password)}";
    byte[] dataBytes = Encoding.UTF8.GetBytes(postData);
    var stream = new MemoryStream(dataBytes);
    
    • 使用匿名类型创建表单数据。

    • 使用Uri.EscapeDataString确保数据正确URL编码。

    • 将编码后的数据转换为字节数组并存入MemoryStream

  2. 设置请求头

    string contentType = "application/x-www-form-urlencoded";
    
    • 设置Content-Typeapplication/x-www-form-urlencoded

  3. 创建并执行POST请求

    var request = webView21.CoreWebView2.Environment.CreateWebResourceRequest(
        "https://www.example.com/login", // 替换为目标URL
        "POST",
        stream,
        $"Content-Type: {contentType}");
    
    webView21.CoreWebView2.NavigateWithWebResourceRequest(request);
    
    • 使用CreateWebResourceRequest方法创建POST请求。

    • 使用NavigateWithWebResourceRequest方法执行POST请求。

  4. 处理导航完成事件

    private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
    {
        if (e.IsSuccess)
        {
            MessageBox.Show("POST请求执行完成,导航成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
            // 这里可以进一步处理响应内容,需要使用WebResourceRequested事件来拦截响应
        }
        else
        {
            MessageBox.Show($"导航失败,错误代码: {e.WebErrorStatus}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    
    • 检查导航是否成功,并根据结果向用户反馈。

  5. 拦截响应内容

    private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
    {
        if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
        {
            var response = e.Response;
            if (response != null)
            {
                // 读取响应内容
                using (var stream = response.Content)
                using (var reader = new StreamReader(stream))
                {
                    string responseBody = await reader.ReadToEndAsync();
                    MessageBox.Show($"响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
    }
    
    • 使用WebResourceRequested事件拦截特定请求的响应。

    • 读取响应内容并向用户展示。


4. 完整示例:在WinForms中集成WebView2并执行普通表单POST请求

为了更清晰地展示如何在WinForms应用程序中集成WebView2控件并执行普通表单类型的POST请求,以下是一个完整的示例项目。该示例包括使用两种方法发送POST请求:通过NavigateWithWebResourceRequest和通过JavaScript。

步骤1:创建新的WinForms项目

  1. 打开Visual Studio

  2. 创建新项目

    • 选择 “Windows Forms 应用程序 (.NET Framework)”“.NET Core”,点击 “下一步”

    • 配置项目名称(例如 WebView2FormPostExample)和位置,点击 “创建”

步骤2:安装WebView2 NuGet包

  1. 打开NuGet包管理器

    • 右键点击项目,选择 “管理NuGet程序包…”

  2. 搜索并安装Microsoft.Web.WebView2

    • “浏览” 标签下,搜索 Microsoft.Web.WebView2

    • 选择 Microsoft.Web.WebView2 包(由Microsoft发布),点击 “安装”

    • 接受许可协议,等待安装完成。

步骤3:设计界面

  1. 打开设计器视图

    • 在解决方案资源管理器中,双击 Form1.cs,进入设计器视图。

  2. 添加WebView2控件

    • 如果WebView2控件已显示在工具箱中,直接拖放到窗体上。

    • 否则,手动添加:

      • 右键点击 “工具箱”,选择 “选择项…”

      • “.NET Framework 组件” 选项卡中,找到 Microsoft.Web.WebView2.WinForms.WebView2

      • 勾选该控件,点击 “确定”

    • 将WebView2控件调整为适合窗体的大小(例如填满顶部区域)。

  3. 添加按钮控件

    • 从工具箱中拖放两个 Button 控件到窗体上。

    • 设置按钮的属性:

      • Button1

        • NamebtnNavigatePost

        • Text执行NavigateWithWebResourceRequest POST

      • Button2

        • NamebtnJavaScriptPost

        • Text执行JavaScript普通表单POST

  4. 添加文本框控件

    • 从工具箱中拖放一个 TextBox 控件到窗体上。

    • 设置文本框的属性:

      • NametextBoxResponse

      • MultilineTrue

      • ScrollBarsVertical

      • AnchorTop, Bottom, Left, Right

    • 调整文本框的大小以适应显示响应内容。

步骤4:编写代码

Form1.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Windows.Forms;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;

namespace WebView2FormPostExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            InitializeAsync();
        }

        private async void InitializeAsync()
        {
            try
            {
                // 初始化 WebView2
                await webView21.EnsureCoreWebView2Async(null);

                // 订阅导航完成事件(仅用于方法一)
                webView21.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;

                // 订阅 WebResourceRequested 事件以拦截响应(仅用于方法一)
                webView21.CoreWebView2.WebResourceRequested += CoreWebView2_WebResourceRequested;

                // 订阅 WebMessageReceived 事件以接收JavaScript发送的消息(仅用于方法二)
                webView21.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;

                // 导航到一个空白页面,准备执行POST请求
                webView21.CoreWebView2.Navigate("about:blank");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"WebView2 初始化失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 方法一:使用 NavigateWithWebResourceRequest 执行普通表单POST请求
        private async void btnNavigatePost_Click(object sender, EventArgs e)
        {
            try
            {
                // 确保WebView2已初始化
                if (webView21.CoreWebView2 == null)
                {
                    MessageBox.Show("WebView2尚未初始化。请稍候再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // 构建URL编码的表单数据
                var formData = new
                {
                    username = "zhangsan",
                    password = "password123"
                };
                string postData = $"username={Uri.EscapeDataString(formData.username)}&password={Uri.EscapeDataString(formData.password)}";
                byte[] dataBytes = Encoding.UTF8.GetBytes(postData);
                var stream = new MemoryStream(dataBytes);

                // 设置请求头
                string contentType = "application/x-www-form-urlencoded";

                // 创建请求
                var request = webView21.CoreWebView2.Environment.CreateWebResourceRequest(
                    "https://www.example.com/login", // 替换为目标URL
                    "POST",
                    stream,
                    $"Content-Type: {contentType}");

                // 执行POST请求
                webView21.CoreWebView2.NavigateWithWebResourceRequest(request);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"执行POST请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 方法二:通过JavaScript执行普通表单POST请求
        private async void btnJavaScriptPost_Click(object sender, EventArgs e)
        {
            try
            {
                // 确保WebView2已初始化
                if (webView21.CoreWebView2 == null)
                {
                    MessageBox.Show("WebView2尚未初始化。请稍候再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // 构建JavaScript代码,使用Fetch API发送application/x-www-form-urlencoded POST请求
                string script = @"
                    (async () => {
                        try {
                            const params = new URLSearchParams();
                            params.append('username', 'zhangsan');
                            params.append('password', 'password123');

                            const response = await fetch('https://www.example.com/login', { // 替换为目标URL
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/x-www-form-urlencoded'
                                },
                                body: params.toString()
                            });

                            if (!response.ok) {
                                throw new Error('网络响应不是OK');
                            }

                            const result = await response.text(); // 根据需要选择解析方式
                            window.chrome.webview.postMessage(JSON.stringify({ success: true, data: result }));
                        } catch (error) {
                            window.chrome.webview.postMessage(JSON.stringify({ success: false, error: error.message }));
                        }
                    })();
                ";

                // 执行JavaScript代码
                await webView21.CoreWebView2.ExecuteScriptAsync(script);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"执行POST请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 处理方法一的导航完成事件
        private async void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
            if (e.IsSuccess)
            {
                MessageBox.Show("方法一:POST请求执行完成,导航成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                // 这里可以进一步处理响应内容,需要使用WebResourceRequested事件来拦截响应
            }
            else
            {
                MessageBox.Show($"方法一:导航失败,错误代码: {e.WebErrorStatus}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 拦截响应内容(方法一)
        private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
        {
            if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
            {
                var response = e.Response;
                if (response != null)
                {
                    // 读取响应内容
                    using (var stream = response.Content)
                    using (var reader = new StreamReader(stream))
                    {
                        string responseBody = await reader.ReadToEndAsync();
                        MessageBox.Show($"方法一:响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
        }

        // 处理方法二的WebMessageReceived事件
        private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
            try
            {
                string message = e.TryGetWebMessageAsString();
                var response = JsonSerializer.Deserialize<Dictionary<string, object>>(message);

                if (response.ContainsKey("success") && (bool)response["success"])
                {
                    string data = response["data"].ToString();
                    textBoxResponse.Text = data;
                    MessageBox.Show("方法二:成功获取POST响应!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else if (response.ContainsKey("error"))
                {
                    string error = response["error"].ToString();
                    MessageBox.Show($"方法二:POST请求错误: {error}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"方法二:处理响应失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

Form1.Designer.cs

namespace WebView2FormPostExample
{
    partial class Form1
    {
        private Microsoft.Web.WebView2.WinForms.WebView2 webView21;
        private System.Windows.Forms.Button btnNavigatePost;
        private System.Windows.Forms.Button btnJavaScriptPost;
        private System.Windows.Forms.TextBox textBoxResponse;

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
            this.btnNavigatePost = new System.Windows.Forms.Button();
            this.btnJavaScriptPost = new System.Windows.Forms.Button();
            this.textBoxResponse = new System.Windows.Forms.TextBox();
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
            this.SuspendLayout();
            // 
            // webView21
            // 
            this.webView21.CreationProperties = null;
            this.webView21.DefaultBackgroundColor = System.Drawing.Color.White;
            this.webView21.Location = new System.Drawing.Point(12, 12);
            this.webView21.Name = "webView21";
            this.webView21.Size = new System.Drawing.Size(776, 300);
            this.webView21.TabIndex = 0;
            this.webView21.ZoomFactor = 1D;
            // 
            // btnNavigatePost
            // 
            this.btnNavigatePost.Location = new System.Drawing.Point(12, 318);
            this.btnNavigatePost.Name = "btnNavigatePost";
            this.btnNavigatePost.Size = new System.Drawing.Size(250, 30);
            this.btnNavigatePost.TabIndex = 1;
            this.btnNavigatePost.Text = "执行NavigateWithWebResourceRequest POST";
            this.btnNavigatePost.UseVisualStyleBackColor = true;
            this.btnNavigatePost.Click += new System.EventHandler(this.btnNavigatePost_Click);
            // 
            // btnJavaScriptPost
            // 
            this.btnJavaScriptPost.Location = new System.Drawing.Point(268, 318);
            this.btnJavaScriptPost.Name = "btnJavaScriptPost";
            this.btnJavaScriptPost.Size = new System.Drawing.Size(250, 30);
            this.btnJavaScriptPost.TabIndex = 2;
            this.btnJavaScriptPost.Text = "执行JavaScript普通表单POST";
            this.btnJavaScriptPost.UseVisualStyleBackColor = true;
            this.btnJavaScriptPost.Click += new System.EventHandler(this.btnJavaScriptPost_Click);
            // 
            // textBoxResponse
            // 
            this.textBoxResponse.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
                                                                            | System.Windows.Forms.AnchorStyles.Left) 
                                                                            | System.Windows.Forms.AnchorStyles.Right)));
            this.textBoxResponse.Location = new System.Drawing.Point(12, 354);
            this.textBoxResponse.Multiline = true;
            this.textBoxResponse.Name = "textBoxResponse";
            this.textBoxResponse.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxResponse.Size = new System.Drawing.Size(776, 84);
            this.textBoxResponse.TabIndex = 3;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.textBoxResponse);
            this.Controls.Add(this.btnJavaScriptPost);
            this.Controls.Add(this.btnNavigatePost);
            this.Controls.Add(this.webView21);
            this.Name = "Form1";
            this.Text = "WebView2 执行普通表单POST请求示例";
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }
    }
}

代码解释

  1. 方法一:使用 NavigateWithWebResourceRequest 执行普通表单POST请求

    • 构建URL编码的表单数据

      var formData = new
      {
          username = "zhangsan",
          password = "password123"
      };
      string postData = $"username={Uri.EscapeDataString(formData.username)}&password={Uri.EscapeDataString(formData.password)}";
      byte[] dataBytes = Encoding.UTF8.GetBytes(postData);
      var stream = new MemoryStream(dataBytes);
      
      • 使用匿名类型创建表单数据。

      • 使用Uri.EscapeDataString确保数据正确URL编码。

      • 将编码后的数据转换为字节数组并存入MemoryStream

    • 设置请求头

      string contentType = "application/x-www-form-urlencoded";
      
      • 设置Content-Typeapplication/x-www-form-urlencoded

    • 创建并执行POST请求

      var request = webView21.CoreWebView2.Environment.CreateWebResourceRequest(
          "https://www.example.com/login", // 替换为目标URL
          "POST",
          stream,
          $"Content-Type: {contentType}");
      
      webView21.CoreWebView2.NavigateWithWebResourceRequest(request);
      
      • 使用CreateWebResourceRequest方法创建POST请求。

      • 使用NavigateWithWebResourceRequest方法执行POST请求。

    • 处理导航完成事件

      private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
      {
          if (e.IsSuccess)
          {
              MessageBox.Show("方法一:POST请求执行完成,导航成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
              // 这里可以进一步处理响应内容,需要使用WebResourceRequested事件来拦截响应
          }
          else
          {
              MessageBox.Show($"方法一:导航失败,错误代码: {e.WebErrorStatus}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
      }
      
      • 检查导航是否成功,并根据结果向用户反馈。

    • 拦截响应内容

      private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
      {
          if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
          {
              var response = e.Response;
              if (response != null)
              {
                  // 读取响应内容
                  using (var stream = response.Content)
                  using (var reader = new StreamReader(stream))
                  {
                      string responseBody = await reader.ReadToEndAsync();
                      MessageBox.Show($"方法一:响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                  }
              }
          }
      }
      
      • 使用WebResourceRequested事件拦截特定请求的响应。

      • 读取响应内容并向用户展示。

  2. 方法二:通过JavaScript在WebView2中执行普通表单POST请求

    • 构建JavaScript代码

      (async () => {
          try {
              const params = new URLSearchParams();
              params.append('username', 'zhangsan');
              params.append('password', 'password123');
      
              const response = await fetch('https://www.example.com/login', { // 替换为目标URL
                  method: 'POST',
                  headers: {
                      'Content-Type': 'application/x-www-form-urlencoded'
                  },
                  body: params.toString()
              });
      
              if (!response.ok) {
                  throw new Error('网络响应不是OK');
              }
      
              const result = await response.text(); // 根据需要选择解析方式
              window.chrome.webview.postMessage(JSON.stringify({ success: true, data: result }));
          } catch (error) {
              window.chrome.webview.postMessage(JSON.stringify({ success: false, error: error.message }));
          }
      })();
      
      • URLSearchParams:构建URL编码的表单数据。

      • Fetch API:发送POST请求,body为URL编码的表单数据。

      • 处理响应:将响应结果通过postMessage发送回C#应用程序。

      • 错误处理:捕获错误并通过postMessage发送错误信息。

    • 执行JavaScript代码

      private async void btnJavaScriptPost_Click(object sender, EventArgs e)
      {
          try
          {
              // 确保WebView2已初始化
              if (webView21.CoreWebView2 == null)
              {
                  MessageBox.Show("WebView2尚未初始化。请稍候再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                  return;
              }
      
              // 构建JavaScript代码,使用Fetch API发送application/x-www-form-urlencoded POST请求
              string script = @"
                  (async () => {
                      try {
                          const params = new URLSearchParams();
                          params.append('username', 'zhangsan');
                          params.append('password', 'password123');
      
                          const response = await fetch('https://www.example.com/login', { // 替换为目标URL
                              method: 'POST',
                              headers: {
                                  'Content-Type': 'application/x-www-form-urlencoded'
                              },
                              body: params.toString()
                          });
      
                          if (!response.ok) {
                              throw new Error('网络响应不是OK');
                          }
      
                          const result = await response.text(); // 根据需要选择解析方式
                          window.chrome.webview.postMessage(JSON.stringify({ success: true, data: result }));
                      } catch (error) {
                          window.chrome.webview.postMessage(JSON.stringify({ success: false, error: error.message }));
                      }
                  })();
              ";
      
              // 执行JavaScript代码
              await webView21.CoreWebView2.ExecuteScriptAsync(script);
          }
          catch (Exception ex)
          {
              MessageBox.Show($"执行POST请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
      }
      
    • 处理WebMessageReceived事件

      private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
      {
          try
          {
              string message = e.TryGetWebMessageAsString();
              var response = JsonSerializer.Deserialize<Dictionary<string, object>>(message);
      
              if (response.ContainsKey("success") && (bool)response["success"])
              {
                  string data = response["data"].ToString();
                  textBoxResponse.Text = data;
                  MessageBox.Show("方法二:成功获取POST响应!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
              }
              else if (response.ContainsKey("error"))
              {
                  string error = response["error"].ToString();
                  MessageBox.Show($"方法二:POST请求错误: {error}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
              }
          }
          catch (Exception ex)
          {
              MessageBox.Show($"方法二:处理响应失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
      }
      
      • 接收消息:通过WebMessageReceived事件接收来自网页的消息。

      • 解析消息:将接收到的JSON字符串反序列化为字典对象。

      • 处理响应:根据响应内容显示消息或错误信息。


5. 注意事项

  1. 跨域请求(CORS)

    • 问题:如果POST请求的目标URL与当前加载的页面不同域,可能会遇到CORS(跨域资源共享)限制,导致请求失败。

    • 解决方法

      • 确保目标服务器已正确设置CORS头,允许来自应用程序的请求。

      • 如果控制目标服务器,可以在服务器端添加必要的CORS配置。

      • 使用同域策略,或通过代理服务器中转请求。

  2. 响应内容处理

    • 方法一NavigateWithWebResourceRequest 方法不直接提供响应内容。要获取响应内容,需要使用WebView2的拦截器功能。

      • 实现:使用WebResourceRequested事件拦截请求,并读取响应内容。

    • 方法二:通过JavaScript执行POST请求,可以更方便地获取响应内容,并通过postMessage将其传回WinForms应用程序。

  3. 安全性

    • 信任来源:确保仅向受信任的API端点发送POST请求,避免潜在的安全风险。

    • 数据验证:在应用程序中验证和处理从网页或API接收的数据,防止潜在的注入攻击。

  4. 错误处理

    • 捕获异常:在执行POST请求和处理响应时,确保捕获并处理可能出现的异常,提升应用程序的稳定性。

    • 反馈用户:在发生错误时,向用户提供明确的错误信息和可能的解决方案。

  5. 性能优化

    • 避免频繁请求:合理安排POST请求的频率,避免过于频繁的请求导致性能下降或被目标服务器限制。

    • 资源管理:确保在不需要时释放WebView2控件的资源,避免内存泄漏。

  6. 字符转义

    • 构建JavaScript代码时:确保URL编码的表单数据中的特殊字符(如&, =, %)已正确转义,以避免JavaScript语法错误。

      • 使用Uri.EscapeDataString进行URL编码。

      • 在JavaScript中使用URLSearchParams或类似方法处理数据。


6. 常见问题及解决方法

问题1:使用 NavigateWithWebResourceRequest 方法后,无法获取POST响应内容

原因

  • NavigateWithWebResourceRequest 方法用于导航,而非直接获取响应内容。要获取响应内容,需要使用WebView2的拦截器功能。

解决方法

  1. 使用WebResourceRequested事件拦截响应

    webView21.CoreWebView2.WebResourceRequested += CoreWebView2_WebResourceRequested;
    
  2. 实现事件处理程序以捕获响应内容

    private async void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
    {
        if (e.Request.Uri == "https://www.example.com/login" && e.Request.Method == "POST")
        {
            var response = e.Response;
            if (response != null)
            {
                using (var stream = response.Content)
                using (var reader = new StreamReader(stream))
                {
                    string responseBody = await reader.ReadToEndAsync();
                    MessageBox.Show($"响应内容: {responseBody}", "响应", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
    }
    

注意:读取响应内容需要处理流的异步读取和线程安全问题,建议使用异步方法。

问题2:通过JavaScript执行POST请求时,postMessage未接收响应

原因

  • JavaScript代码存在错误。

  • WebMessageReceived事件未正确订阅或处理。

  • CORS限制导致请求失败。

解决方法

  1. 检查JavaScript代码是否正确

    • 使用WebView2的DevTools(按F12)查看控制台是否有错误。

    • 确保Fetch API请求的URL和参数正确。

  2. 确认WebMessageReceived事件已正确订阅

    webView21.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
    
  3. 处理CORS问题

    • 确保目标服务器允许来自应用程序的跨域请求。

    • 检查网络响应,确认请求是否成功。

问题3:JSON反序列化失败或返回的HTML包含转义字符

原因

  • 返回的JSON格式不正确。

  • 未正确解析JSON字符串。

解决方法

  • 使用正确的JSON解析方法,例如:

    string htmlContent = JsonSerializer.Deserialize<string>(result);
    
  • 确保JavaScript返回的字符串已正确序列化:

    window.chrome.webview.postMessage(JSON.stringify(data));
    

问题4:WebView2控件报错“未能解析类型 Microsoft.Web.WebView2.WinForms.WebView2”

解决方法

  1. 确认NuGet包已正确安装

    • 在解决方案资源管理器中,展开 “依赖项” > “NuGet”,确保 Microsoft.Web.WebView2 包已列出。

  2. 检查项目目标框架

    • 在项目属性中,确保目标框架为 .NET Framework 4.6.2 及以上,或 .NET Core/5+/6+。

  3. 重启Visual Studio

    • 有时,安装NuGet包后需要重启Visual Studio才能识别新引用。

  4. 清理并重建解决方案

    • 选择 “生成” > “清理解決方案”,然后 “生成” > “生成解决方案”

  5. 手动添加引用

    • 在设计器视图中,确保WebView2控件已添加到工具箱并拖放到窗体上。


7. 参考资料


8. 总结

在WinForms应用程序中使用WebView2控件执行普通表单类型的HTTP POST请求,可以通过多种方法实现,包括使用NavigateWithWebResourceRequest方法和通过JavaScript执行POST请求。根据具体需求和场景,选择合适的方法来实现数据提交和响应处理。

关键要点

  1. 集成WebView2控件:确保WebView2控件已正确添加到项目中,并已初始化。

  2. 选择POST方法

    • 方法一:使用NavigateWithWebResourceRequest构建和执行普通表单POST请求。

    • 方法二:通过JavaScript在网页上下文中执行普通表单POST请求,并通过postMessage与应用程序通信。

  3. 处理响应:根据选择的方法,使用适当的事件和方法来获取和处理POST请求的响应内容。

  4. 错误处理与安全性:确保添加必要的错误处理机制,并遵循安全最佳实践,保护应用程序和用户数据。

  5. 性能优化:合理安排POST请求的频率,避免过于频繁的请求导致性能下降或被目标服务器限制。