CefSharp.ChromiumWebBrowser瀏覽器的一些功能使用

1.配置支持AnyCpu編譯模式

2.使用Http代理服務

3.Cookie隔離,每個IWebBrowser實例的數據不共享

4.使用IResponseFilter獲取響應數據

 

1.配置支持AnyCpu編譯模式

  CefSharp從51版本以後開始支持AnyCpu編譯模式,首先需要在當前項目的csproj文件的PropertyGroup節點下第一行增加一個配置項

<CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport>

然後在程序的啓動入口配置動態加載目標平臺x86/x64的程序集:

   [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            AppDomain.CurrentDomain.AssemblyResolve += Resolver;
            Application.Run(new Form1());
        }

        private static Assembly Resolver(object sender, ResolveEventArgs args)
        {
            if (args.Name.StartsWith("CefSharp"))
            {
                string assemblyName = args.Name.Split(new[] { ',' }, 2)[0] + ".dll";
                string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                                                       Environment.Is64BitProcess ? "x64" : "x86",
                                                       assemblyName);
                return File.Exists(archSpecificPath)
                           ? Assembly.LoadFile(archSpecificPath)
                           : null;
            }
            return null;
        }

 

  這種方法是根據運行的目標平臺動態去加載對應的程序集,如果我們能明確運行平臺則可以不用加上面的代碼邏輯,在當前項目App.config文件的根節點下加入以下配置即可:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <probing privatePath="x64"/><!--如果是32位系統,則換成x86-->
  </assemblyBinding>
</runtime>

 

2.使用Http代理服務

網上一些文章介紹的通過添加命令行參數CefCommandLineArgs的方式,我試了一下不管用,通過 CefSharpSettings.Proxy =  new ProxyOptions("ipadress", "prot", "username",  "password");  這句是可以配置成功的,但這個是全局配置,不能滿足獨立我要控制每個browser實例各自使用自己的代理服務器。通過在初始化ChromiumWebBrowser的地方加入以下代碼可實現動態設置代理。

var browser = new ChromiumWebBrowser("url", context); 
browser.RequestHandler = new DefaultRequestHandler(); Cef.UIThreadTaskFactory.StartNew(delegate { var rc = browser.GetBrowser().GetHost().RequestContext; rc.GetAllPreferences(true); var dict = new Dictionary<string, object>(); dict.Add("mode", "fixed_servers"); dict.Add("server", "ipaddress:prot"); //此處替換成實際 ip地址:端口 string error; bool success = rc.SetPreference("proxy", dict, out error); if (!success) { Console.WriteLine("something happen with the prerence set up" + error); } });

如果代理服務有用戶名密碼的話,則需要在DefaultRequestHandler類裏重寫GetAuthCredentials方法,如下:

 protected override bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
 {
      if (isProxy)
      {
           callback.Continue("username", "passwrod");
           return true;
      }
      return false;
}

這樣就可以實現每個ChromiumWebBrowser運行實例獨立連接自己的代理服務器了。

3.Cookie隔離,每個IWebBrowser實例的數據不共享

  要保證多個browser實例之間cookie不共享,就不要在全局設置CefSettings中設置CachePath值,應該在實例的RequestContextSettings中設置,可以設置成每個browser擁有獨立的緩存目錄。在RequestContext內添加的cookie只有當前browser才能訪問,從而實現cookie隔離。

 var setting = new RequestContextSettings()
                {
                    CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CefSharp\\Cache_" + name),
                    PersistSessionCookies = true,
                    PersistUserPreferences = true
                };
                var context = new RequestContext(setting);
                var cookieManager = context.GetCookieManager(null);
          //這樣設置的cookie不是全局的,只有當前browser才能訪問
                cookieManager.SetCookie("domain", new Cookie
                {
                    Name = "cookiename",
                    Value = "cookievalue",
                    Path = "/"
                });
                var browser = new ChromiumWebBrowser("url", context);

4.使用IResponseFilter獲取響應數據

   要獲取ChromiumWebBrowser中每個請求的響應body內容並不是那麼方便,拿到Frame的html內容倒是很簡單,使用IFrame的GetSourceAsync()方法就行,但有時候我們需要單個請求的響應結果,這就需要自定義實現IResponseFilter接口來實現響應數據的攔截。

    
  //DefaultResourceHandler的構造可以放在IRequestHandler的實現類的GetResourceRequestHandler方法內
  class DefaultResourceHandler : ResourceRequestHandler
    {
        protected override IResponseFilter GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            if (response.MimeType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
            {
                return JsonResponseFilter.CreateFilter(request.Identifier.ToString());
            }
            return null;
        }

        protected override void OnResourceLoadComplete(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
            var filter = JsonResponseFilter.GetFileter(request.Identifier.ToString()) as JsonResponseFilter;
            if (filter != null)
            {
                var encode = !string.IsNullOrEmpty(response.Charset)
                    ? Encoding.GetEncoding(response.Charset)
                    : Encoding.UTF8;
                using (var read = new StreamReader(filter.GetStream(), encode))
                {
                    var text = read.ReadToEnd();
                    Debug.WriteLine(text);
                }
            }
        }
    }

    public class JsonResponseFilter : IResponseFilter
    {
        private MemoryStream Stream;

        public JsonResponseFilter()
        {
            Stream = new MemoryStream();
        }

        public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)
        {
            try
            {
                if (dataIn == null || dataIn.Length == 0)
                {
                    dataInRead = 0;
                    dataOutWritten = 0;

                    return FilterStatus.Done;
                }

                dataInRead = dataIn.Length;
                dataOutWritten = Math.Min(dataInRead, dataOut.Length);

                dataIn.CopyTo(dataOut);
                dataIn.Seek(0, SeekOrigin.Begin);
                byte[] bs = new byte[dataIn.Length];
                dataIn.Read(bs, 0, bs.Length);

                Stream.Write(bs, 0, bs.Length);

                dataInRead = dataIn.Length;
                dataOutWritten = dataIn.Length;

                return FilterStatus.NeedMoreData;
            }
            catch (Exception ex)
            {
                dataInRead = dataIn.Length;
                dataOutWritten = dataIn.Length;

                return FilterStatus.Done;
            }
        }

        public bool InitFilter()
        {
            return true;
        }

        public Stream GetStream()
        {
            Stream.Seek(0, SeekOrigin.Begin);
            return Stream;
        }

        public void Dispose()
        {
        }

        private static Dictionary<string, IResponseFilter> _dictionary = new Dictionary<string, IResponseFilter>();

        public static IResponseFilter CreateFilter(string id)
        {
            var filter = new JsonResponseFilter();
            _dictionary[id] = filter;
            return filter;
        }

        public static IResponseFilter GetFileter(string id)
        {
            if (_dictionary.ContainsKey(id))
            {
                var filter = _dictionary[id];
                _dictionary.Remove(id);
                return filter;
            }
            return null;
        }
    }

  爲了截獲響應數據繞了這麼一大圈有點費勁,不過人家這種設計也是爲了方便外部擴展,可以針對不同響應類型的response來實現IResponseFilter過濾器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章