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

时间:2015-12-28 15:51:40

标签: c# http header

我正在使用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,则不会包含基本授权标题。

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

3 个答案:

答案 0 :(得分:4)

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

您只需要相关的界面。最简单的方法是添加对 Microsoft Internet Controls 的引用(您将在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";
}

答案 1 :(得分:1)

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

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代码撰写您的请求。

答案 2 :(得分:0)

在处理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;