Code Bye

CefSharp 中文帮助文档(三):请求流程处理

大多使用CefSharp进行开发的软件都不是单纯用作浏览器的,一般都会对请求和响应进行处理,这里介绍几种常见用法:

3.1 读取网页源代码

在页面加载完成后处理, 依赖最低环境 4.5.2

async void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)  
{  
    Log.WriteLog("browser_FrameLoadEnd:" + e.Url);  

    var result = await browser.GetSourceAsync();  
}

如果想在4.0下环境操作需要使用。

 var task = browser.GetSourceAsync();  
 task.Wait();  
 string content = task.Result;

3.2 获取页面中的指定文件内容(.jpg,.js等)

A.首先需要对ChromiumWebBrowser 的 IRequestHandler RequestHandler进行实现。
B.需要对 IRequestHandler 的IResponseFilter IRequestHandler.GetResourceResponseFilter 方法进行重写。
C.需要写一个类实现 IResponseFilter 接口。
D.然后就可用在IResponseFilter 的
FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten) 方法中,读取指定内容的流了。

3.3 过滤某些页面内容,例如图片或某些文字

具体实现,参考 3.2 ,在最后的Filter方法中,对返回的dataOut不进行赋值或者,取到值,然后replace处理,返回其他数据,即可。

3.4 文件进一步获取,获取完整内容

说明:由于很多文件无法获取到完整内容,再者具体文件内容在Filter里面进行了控制,而Fileter的内容依赖于IRequestHandler,所以,外部只能操作Handler得到数据。 所以需要在,Filter和Hanlder类中,使用事件来传递具体的内容。代码如下。 Filter类如下:

public class TestImageFilter : IResponseFilter  
    {  
        public event Action<byte[]> NotifyData;  
        private int contentLength = 0;  
        private List<byte> dataAll = new List<byte>();  

        public void SetContentLength(int contentLength)  
        {  
            this.contentLength = contentLength;  
        }  

        public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)  
        {  
            try  
            {  
                if (dataIn == null)  
                {  
                    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);  
                dataAll.AddRange(bs);  

                if (dataAll.Count == this.contentLength)  
                {  
                    // 通过这里进行通知  
                    NotifyData(dataAll.ToArray());  

                    return FilterStatus.Done;  
                }  
                else if (dataAll.Count < this.contentLength)  
                {  
                    dataInRead = dataIn.Length;  
                    dataOutWritten = dataIn.Length;  

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

                return FilterStatus.Done;  
            }  
        }  

        public bool InitFilter()  
        {  
            return true;  
        }  
    }

Filter类有了,那我们如何知道数据流的具体长度呢?这就需要在Handler的实现的其他方法里面寻找了。

        bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)  
        {  
            //NOTE: You cannot modify the response, only the request  
            // You can now access the headers  
            //var headers = response.ResponseHeaders;  
            try  
            {  
                var content_length = int.Parse(response.ResponseHeaders["Content-Length"]);  
                if (this.filter != null)  
                {  
                    this.filter.SetContentLength(content_length);  
                }  
            }  
            catch { }  
            return false;  
        }  

        private TestImageFilter filter = null;  
        public event Action<byte[]> NotifyData;  

        IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)  
        {  
            var url = new Uri(request.Url);  
            if (url.AbsoluteUri.Contains("http://test.test.com/somehead?"))  
            {  
                this.filter = new TestImageFilter();  
                filter.NotifyData += filter_NotifyData;  

                return filter;  
            }  

            return null;  
        }  

        void filter_NotifyData(byte[] data)  
        {  
            if (NotifyData != null)  
            {  
                NotifyData(data);  
            }  
        }

此方法位IRequestHandler的一部分实现,通过实现函数:IRequestHandler.GetResourceResponseFilter得到资源文件的长度,然后长度传入Filter,在Filter中控制从而得到整个数据的真正长度。

3.5 文件进一步获取,获取完整内容(优化,Content-Length不一致)

由于,部分站点,返回数据是分片了的,即:不能通过,Content-Length的长度来判断,程序的流是否完成。
所以需要其他方式处理,即:单个http请求完成的时候,会调用Complete方法,所以可以在这里处理。
下面是测试代码: Filter管理类

public class FilterManager
    {
        private static Dictionary<string, IResponseFilter> dataList = new Dictionary<string, IResponseFilter>();

        public static IResponseFilter CreateFilter(string guid)
        {
            lock (dataList)
            {
                var filter = new TestImageFilter();
                dataList.Add(guid, filter);

                return filter;
            }
        }

        public static IResponseFilter GetFileter(string guid)
        {
            lock (dataList)
            {
                return dataList[guid];
            }
        }
    }

TestFilter类,对流进行合并

public class TestImageFilter : IResponseFilter
    {
        public List<byte> dataAll = new List<byte>();

        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);
                dataAll.AddRange(bs);

                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;
        }
    }

最后是部分的。IRequestHandler实现代码

public class RequestHandler : IRequestHandler
    {
        // 略去代码 ...

        public event Action<byte[]> NotifyMsg;

        IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            var url = new Uri(request.Url);
            if (url.AbsoluteUri.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
            {
                var filter = FilterManager.CreateFilter(request.Identifier.ToString());

                return filter;
            }

            return null;
        }

        void filter_NotifyData(byte[] data)
        {
            if (NotifyMsg != null)
            {
                NotifyMsg(data);
            }
        }

        void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
            if (request.Url.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
            {
                var filter = FilterManager.GetFileter(request.Identifier.ToString()) as TestImageFilter;

                filter_NotifyData(filter.dataAll.ToArray());
            }
        }
    }

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明CefSharp 中文帮助文档(三):请求流程处理