使用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
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步