为C#中的所有请求向Webbrowser控件添加自定义标头

本文关键字:控件 Webbrowser 添加 自定义 请求 | 更新日期: 2023-09-27 18:28:12

我使用的是C#和.NET 3.5。如您所知,我们可以在web浏览器控件上向Navigate()添加自定义标头,如下所示:

var myUrl = "http://example.com/mypage.htm";
System.Uri uri = new Uri(myUrl);
byte[] authData = System.Text.UnicodeEncoding.UTF8.GetBytes("user:password");
string authHeader = 
    "Authorization: Basic " + Convert.ToBase64String(authData) + "'r'n" +
    "User-Agent: MyUserAgent'r'n";
webTDW8961nd.Navigate(uri, "", null, authHeader);

在上面的示例中,我们为单个导航设置了"基本授权"标头。

现在让我们谈谈重定向。如果我们想执行重定向到另一个页面的javascript,那么基本授权标头将不包括在内。

您的解决方案是什么?如何添加一个适用于所有请求而不是一次的标头?

为C#中的所有请求向Webbrowser控件添加自定义标头

问题是,虽然WinForm和WPF的WebBrowser都只是ActiveX IE控件的一个相对较薄的包装器,但它们并没有向我们公开所有感兴趣的事件(第二个提供的事件甚至比第一个更少)。有两种方法可以解决这个问题:首先,将WF浏览器控件子类化并添加所需内容,或者使用WPF控件并在其中添加钩子。我发现第二种方法在WPF应用程序中更方便。

您只需要相关的接口。最简单的方法是添加对MicrosoftInternetControls的引用(您可以在COM标题下找到VS)。这打开了一个名为SHDocVw的命名空间,其中包含了我们所需要的所有内容(如果出于任何原因,您想摆脱这种依赖关系,只需将使用的P/Invoke接口复制到自己的代码中即可)。

您可以使用反射来获取底层浏览器。如果您过早调用它,它将返回null,所以我将它放入WebBrowser.Navigating处理程序:

using SHDocVw;
var ActiveXInstance = (IWebBrowser2)Browser.GetType().InvokeMember("ActiveXInstance", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, Browser, new object[] { });

一旦你有了它,你就可以用浏览器做好事了。例如,您可以使用不直接公开的各种属性和方法:

ActiveXInstance.Silent = true; // suppresses script error dialogs

并添加丢失的事件挂钩:

var SetupEvents2 = (DWebBrowserEvents2_Event)ActiveXInstance;
SetupEvents2.BeforeNavigate2 += OnBeforeNavigate2;

有两个事件接口,2变体包含较新的事件。你可以在MSDN上查找所有这些。

然后,回到标题:BeforeNavigate2事件允许您将额外的标题放入所提供的对象:

private void OnBeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel) {
  Headers = $"Accept-Language: XX;en'r'n";
}

要为每个请求添加自定义标头,可以实现扩展方法:

public static class WebBrowserExtensions
{
    public static void NavigateWithAuthorization(this WebBrowser browser, Uri uri)
    {
        byte[] authData = System.Text.Encoding.UTF8.GetBytes("user:password");
        string authHeader = "Authorization: Basic " + Convert.ToBase64String(authData) + "'r'n" + "User-Agent: MyUserAgent'r'n";
        browser.Navigate(uri, "", null, authHeader);
    }
}

然后将其称为标准方法:

//browser.Navigate(uri, "", null, authHeader);
browser.NavigateWithAuthorization(uri);

第二个问题是关于重定向。但你的场景不会在一个简单的浏览器和小提琴手中工作。这是web协议的功能,当您重定向到另一个Uri时,您会启动具有新属性的新请求。您可以在js代码中编写请求。

在处理BeforeNavigate2时,如果您想包含其他标头,则需要取消当前导航事件,还需要停止浏览器。然后,您需要再次导航到传递其他标头的URL。

以下是我处理基本身份验证的方法,guest可以将其作为该url的用户名和密码进行测试:https://jigsaw.w3.org/HTTP/Basic/

string additionalHeaders;
private void Form1_Load(object sender, EventArgs e)
{
    byte[] authData = System.Text.Encoding.UTF8.GetBytes("guest:guest");
    additionalHeaders = $"Authorization: Basic {Convert.ToBase64String(authData)}'r'n";
    webBrowser1.Navigate("about:blank", null, null, additionalHeaders);
    var wbevents = (DWebBrowserEvents2_Event)webBrowser1.ActiveXInstance;
    wbevents.BeforeNavigate2 += Wbevents_BeforeNavigate2;
    webBrowser1.Navigate("https://jigsaw.w3.org/HTTP/Basic/", null, null, additionalHeaders);
}
private void Wbevents_BeforeNavigate2(object pDisp, ref object URL, ref object Flags,
    ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
{
    if (!$"{Headers}".Contains(additionalHeaders) &&
        TargetFrameName == null &&
        $"{URL}".ToLower().StartsWith("http"))
    {
        Cancel = true;
        ((IWebBrowser2)pDisp).Stop();
        object headers = additionalHeaders + $"{Headers}";
        object url = $"{URL}";
        object flags = null;
        object targetFrameName = $"{TargetFrameName}";
        ((IWebBrowser2)pDisp).Navigate2(ref url, ref flags, 
           ref targetFrameName, ref PostData, ref headers);
    }
}

注意-刷新时不会引发导航

刷新后的浏览器中存在功能/缺陷。刷新浏览器时,不会引发Navigating事件,这意味着刷新时不会添加头。解决方法是禁用快捷方式和上下文菜单:

this.webBrowser1.WebBrowserShortcutsEnabled = false;
this.webBrowser1.IsWebBrowserContextMenuEnabled = false;