使用C#在CEF中拦截并响应请求

一、前言

忙里偷闲,研究了一下如何在CEF中拦截请求,并作出响应。这个功能对某些需要修改服务器响应的需求来说必不可少,可以直接读取本地文件作为响应内容。

C#的CEF封装项目有很多,我使用的是ChromiumFx。它的最大特点是采用事件的形式实现接口的效果。这样我们在使用时只需要订阅事件实现功能即可,而不用继承类重写方法,这很苏胡。

二、CEF3接口

简单介绍一下CEF3的接口。

CefClient:回调管理类,包含5个接口用于创建其它的回调类的对象

CefLifeSpanHandler: 回调类,用于控制popup对话框的创建和关闭等操作

CefLoadHandler: 回调类,可以用来监听frame的加载开始,完成,错误等信息

CefRequestHandler: 回调类,用于监听资源加载,重定向等信息

CefDisplayHandler: 回调类,用于监听页面加载状态,地址变化,标题等得信息

CefGeolocationHandler: 回调类,用于CEF3向嵌入者申请geolocation的权限 CefApp: 与进程,命令行参数,代理,资源管理相关的回调类,用于让CEF3的调用者们定制自己的逻辑

CefBrowser: renderer进程中执行浏览相关的类,例如前进,后退等

CefBrowserHost: browser进程中的执行浏览相关的类,其会把请求发送给CefBrowser

CefFrame: 表示的是页面中的一个Frame,可以加载特定url,在该运行环境下执行JavaScript代码等得。

V8:CEF3提供支持V8extension的接口,但是这有两个限制,第一,v8 extension仅在Renderer进程使用;第二,仅在沙箱模型关闭时使用。

三、流程

我们关注的就是上述的CefRequestHandler接口,其中包含了发起请求,读取响应结果的接口方法。

以下内容在ChromiumFx库上实现,使用其它库大致相似。

1. ChromiumWebBrowser提供了RequestHandler类,在GetResourceHandler中可以根据需要指定ResourceHandler实例以实现拦截响应请求,或返回null来使用cef默认的方式处理请求。

2.新建ResourceHandler实例,并指定以下事件发生时的动作:

ProcessRequest 处理请求,允许请求返回true;取消请求返回false。当需要返回的数据就绪后(自行请求网络或者文件就绪)或立即调用callback.Continue(),通知cef进入下一步:GetResponseHeaders。

GetResponseHeaders 设置响应头,在这里可以设置mime-type,响应长度,其它头等。不确定长度设置响应长度为-1。

ReadResponse 设置响应结果。可以设置响应具体内容,设置已读取长度。cef调用完上一步后会继续调用此方法。根据响应长度和数据就绪情况,调用此方法的次数和策略不同。

当响应长度为-1时,cef无法根据已读长度确定是否已读取完毕,必需根据返回值false来结束读取。

当响应长度大于0时,cef根据每次调用得到的已读长度,或返回值false来结束读取。

如果数据未就绪,可以设置读取长度为0 ,返回true,并在稍后调用callbak.Continue()通知cef调用此方法读取响应内容。

注意:

当响应长度为-1时,必需保证此方法至少执行两次,第一次返回true表示数据全部就绪,第二次返回false表示读取结束。

当响应长度大于0时,设置内容和已读长度,返回true。则此方法只执行一次。

若实际返回的响应内容,长度大于之前设置的响应总长度,则返回内容将被截取。

四、代码

其中还包括了其它设置和订阅了一些有趣的事件,供参考。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        InitializationChromium();
    }
 
    private void InitializationChromium()
    {
        ChromiumWebBrowser.OnBeforeCfxInitialize += ChromiumWebBrowser_OnBeforeCfxInitialize;
        ChromiumWebBrowser.OnBeforeCommandLineProcessing += ChromiumWebBrowser_OnBeforeCommandLineProcessing;
        ChromiumWebBrowser.Initialize();
 
        ChromiumWebBrowser wb = new ChromiumWebBrowser();
        wb.Dock = DockStyle.Fill;
        wb.Parent = this;
        wb.LoadHandler.OnLoadStart += LoadHandler_OnLoadStart;
        wb.LoadHandler.OnLoadEnd += LoadHandler_OnLoadEnd;
        wb.DownloadHandler.OnBeforeDownload += DownloadHandler_OnBeforeDownload;
        wb.RequestHandler.OnBeforeBrowse += RequestHandler_OnBeforeBrowse;
        wb.RequestHandler.GetResourceHandler += RequestHandler_GetResourceHandler;
        wb.SetWebResource("http://localresource/html", new Chromium.WebBrowser.WebResource("<html></html>"));
        wb.LoadUrl("http://m.baidu.com");
    }
 
    void RequestHandler_GetResourceHandler(object sender, Chromium.Event.CfxGetResourceHandlerEventArgs e)
    {
        if (e.Request.Url == "http://m.baidu.com/")
        {
            CfxResourceHandler handler = new CfxResourceHandler();
            handler.ProcessRequest += handler_ProcessRequest;
            handler.GetResponseHeaders += handler_GetResponseHeaders;
            handler.ReadResponse += handler_ReadResponse;
 
            e.SetReturnValue(handler);
        }
        else
        {
            e.SetReturnValue(null);
        }
    }
 
     
    void handler_ProcessRequest(object sender, Chromium.Event.CfxProcessRequestEventArgs e)
    {
        e.Callback.Continue();
        e.SetReturnValue(true);
    }
 
    void handler_GetResponseHeaders(object sender, Chromium.Event.CfxGetResponseHeadersEventArgs e)
    {
        e.ResponseLength = -1;
        e.Response.MimeType = "text/html";
        e.Response.Status = 200;
    }
 
    int count = 0;
    void handler_ReadResponse(object sender, Chromium.Event.CfxReadResponseEventArgs e)
    {
        if (count == 0)
        {
            byte[] data = Encoding.UTF8.GetBytes("<html><body><h1>Hello CEF</h1></body></html>");
            e.BytesRead = data.Length;
            Marshal.Copy(data, 0, e.DataOut, data.Length);
            e.SetReturnValue(true);
            count++;
        }
        else
        {
            e.SetReturnValue(false);
        }
    }
 
    void RequestHandler_OnBeforeBrowse(object sender, Chromium.Event.CfxOnBeforeBrowseEventArgs e)
    {
        if (e.Request.Url == "")
        {
            e.SetReturnValue(true);
        }
    }
 
    void DownloadHandler_OnBeforeDownload(object sender, Chromium.Event.CfxOnBeforeDownloadEventArgs e)
    {
        e.Callback.Continue("d:\\" + e.SuggestedName, false);
    }
 
    void LoadHandler_OnLoadEnd(object sender, Chromium.Event.CfxOnLoadEndEventArgs e)
    {
        //MessageBox.Show("end loading" + e.Frame.Url);
        //MessageBox.Show("is main:" + e.Frame.IsMain);
        e.Frame.SelectAll();
    }
 
    void LoadHandler_OnLoadStart(object sender, Chromium.Event.CfxOnLoadStartEventArgs e)
    {
        //MessageBox.Show("start loading"+e.Frame.Url);
    }
 
 
    void ChromiumWebBrowser_OnBeforeCommandLineProcessing(Chromium.Event.CfxOnBeforeCommandLineProcessingEventArgs e)
    {
        e.CommandLine.AppendSwitch("--disable-web-security");
    }
 
    void ChromiumWebBrowser_OnBeforeCfxInitialize(Chromium.WebBrowser.Event.OnBeforeCfxInitializeEventArgs e)
    {
        e.Settings.CachePath = "Session";
        e.Settings.Locale = "zh-CN";
    }
 
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        CfxRuntime.Shutdown();
    }
}

 

响应长度已知的情况:

复制代码
 1         void handler_GetResponseHeaders(object sender, Chromium.Event.CfxGetResponseHeadersEventArgs e)
 2         {
 3             e.ResponseLength = Encoding.UTF8.GetBytes("<html><body><h1>Hello CEF</h1></body></html>").Length;
 4             e.Response.MimeType = "text/html";
 5             e.Response.Status = 200;
 6         }
 7 
 8         void handler_ReadResponse(object sender, Chromium.Event.CfxReadResponseEventArgs e)
 9         {
10                 byte[] data = Encoding.UTF8.GetBytes("<html><body><h1>Hello CEF</h1></body></html>");
11                 e.BytesRead = data.Length;
12                 Marshal.Copy(data, 0, e.DataOut, data.Length);
13                 e.SetReturnValue(true);
14         }
复制代码

最终效果如下:

 综上所述,虽然学到了新知识,但女朋友不理你还是开心不起来对吧。

 

五、参考资料

https://groups.google.com/forum/#!topic/cefglue/k-ixiAiszYg

https://code.google.com/p/cefpython/source/browse/cefpython/cef3/linux/binaries_64bit/wxpython-response.py?r=26d373f81ca9

http://blog.csdn.net/lee353086/article/details/40779471

 

posted @   超软毛毛虫  阅读(19261)  评论(4编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示