CEF框架:各种各样的Handle(四)——CefURLRequest,发起HTTP请求与处理

本文主要是介绍CEF框架:各种各样的Handle(四)——CefURLRequest,发起HTTP请求与处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • CEF的HTTP请求类
    • CefResourceRequest
    • CefURLRequest
  • CefURLRequest的使用
    • cef_message_route
    • handled:urlrequet的处理类
      • OnQuery
  • CefURLRequestClient

CEF的HTTP请求类

在CEF框架中(Chromium Embedded Framework),CefURLRequestCefResourceRequest确实是两个不同的功能类,它们的主要区别在于使用场景和功能:

CefResourceRequest

  • 这个类通常用于加载资源类型的请求,比如在渲染网页时加载HTML文件、脚本、样式表、图片等。
  • CefResourceRequest主要用于内部处理浏览器的渲染操作,例如当浏览器需要获取并显示一个页面上的资源时。
  • 它通常不会直接暴露给通过CEF构建应用程序的开发者,除非开发者打算实现自定义的资源加载逻辑。
  • 简单的说,这个类用于控制在浏览器渲染时使用,发起和结束都不是程序来控制,只是说可以获得其中的过程数据进行处理,在上一篇CEF框架:各种各样的Handle(三)——拦截Http的请求与响应中已经有详细的说明。

CefURLRequest

  • CefURLRequest是一个更通用的请求类,它可以用于任何类型的HTTP请求,包括异步的和非阻塞的网络访问。
  • 开发者可以使用这个类来执行自定义的HTTP请求,如下载文件、提交表单数据、访问RESTful API等。
  • 它提供了一组回调接口(CefURLRequestClient),使开发者可以对请求过程中的各种事件作出响应,如开始请求、完成请求、错误发生、数据可用等。
  • 也就是说,当服务器需要自己来发起HTTP请求的时候,比如要发起GET,POST,DELETE等请求的时候,就可以使用CefURLRequest来发起。

CefURLRequest的使用

在CEF的框架示例代码中,cefclient就给出了如何使用CefURLRequest类的例子:

这个代码在resource/urlrequest.html文件中,这个文件中关键的代码就是这个按钮,也就是发起http请求的代码:

function execURLRequest() {document.getElementById('ta').value = 'Request pending...';// Results in a call to the OnQuery method in urlrequest_test.cppwindow.cefQuery({request: 'URLRequestTest:' + document.getElementById("url").value,onSuccess: function(response) {document.getElementById('ta').value = response;},onFailure: function(error_code, error_message) {document.getElementById('ta').value = 'Failed with error ' + error_message + ' (' + error_code + ')';}});
}

可以看到,就是调用了CEF框架中封装的cefQuery函数,详情在最早的一篇CEF文章中就提到过:CEF消息传递实战(实测可用,新鲜出炉)。

cef_message_route

在CEF消息传递实战(实测可用,新鲜出炉)中也提到了,是需要定义一个Handler来处理前端JS调用的cefQuery请求的。在CEF的框架示例代码CEFSIMPLE中,可以新定义一个Handler来处理。

在CEFCLIENT中,定义了一个消息的路由中心,这个路由器在cef_message_route文件中:

  • 统一的消息处理类

    class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide
    {...bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message) OVERRIDE {CEF_REQUIRE_UI_THREAD();const std::string& message_name = message->GetName();if (message_name == query_message_name_) {CefRefPtr<CefListValue> args = message->GetArgumentList();DCHECK_EQ(args->GetSize(), 4U);const int context_id = args->GetInt(0);const int request_id = args->GetInt(1);const CefString& request = args->GetString(2);const bool persistent = args->GetBool(3);if (handler_set_.empty()) {// No handlers so cancel the query.CancelUnhandledQuery(browser, frame, context_id, request_id);return true;}const int browser_id = browser->GetIdentifier();const int64 query_id = query_id_generator_.GetNextId();CefRefPtr<CallbackImpl> callback(new CallbackImpl(this, browser_id, query_id, persistent));// Make a copy of the handler list in case the user adds or removes a// handler while we're iterating.HandlerSet handler_set = handler_set_;bool handled = false;HandlerSet::const_iterator it_handler = handler_set.begin();for (; it_handler != handler_set.end(); ++it_handler) {handled = (*it_handler)->OnQuery(browser, frame, query_id, request, persistent,callback.get());if (handled)break;}// If the query isn't handled nothing should be keeping a reference to// the callback.DCHECK(handled || callback->HasOneRef());if (handled) {// Persist the query information until the callback executes.// It's safe to do this here because the callback will execute// asynchronously.QueryInfo* info = new QueryInfo;info->browser = browser;info->frame = frame;info->context_id = context_id;info->request_id = request_id;info->persistent = persistent;info->callback = callback;info->handler = *(it_handler);browser_query_info_map_.Add(browser_id, query_id, info);} else {// Invalidate the callback.callback->Detach();// No one chose to handle the query so cancel it.CancelUnhandledQuery(browser, frame, context_id, request_id);}return true;} else if (message_name == cancel_message_name_) {CefRefPtr<CefListValue> args = message->GetArgumentList();DCHECK_EQ(args->GetSize(), 2U);const int browser_id = browser->GetIdentifier();const int context_id = args->GetInt(0);const int request_id = args->GetInt(1);CancelPendingRequest(browser_id, context_id, request_id);return true;}return false;}...
    }
    

    这段代码中通过Handler调用具体的处理类,handled = (*it_handler) ->OnQuery(browser, frame, query_id, request, persistent, callback.get());

handled:urlrequet的处理类

urlrequet的处理类定义在urlrequest_test.cc类中:

// Handle messages in the browser process. Only accessed on the UI thread.
class Handler : public CefMessageRouterBrowserSide::Handler {public:Handler() { CEF_REQUIRE_UI_THREAD(); }~Handler() { CancelPendingRequest(); }// Called due to cefQuery execution in urlrequest.html.bool OnQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id,const CefString& request,bool persistent,CefRefPtr<Callback> callback) OVERRIDE {CEF_REQUIRE_UI_THREAD();// Only handle messages from the test URL.const std::string& url = frame->GetURL();if (!test_runner::IsTestURL(url, kTestUrlPath))return false;const std::string& message_name = request;if (message_name.find(kTestMessageName) == 0) {const std::string& load_url =message_name.substr(sizeof(kTestMessageName));CancelPendingRequest();DCHECK(!callback_.get());DCHECK(!urlrequest_.get());callback_ = callback;// Create a CefRequest for the specified URL.CefRefPtr<CefRequest> cef_request = CefRequest::Create();cef_request->SetURL(load_url);cef_request->SetMethod("GET");// Callback to be executed on request completion.// It's safe to use base::Unretained() here because there is only one// RequestClient pending at any given time and we explicitly detach the// callback in the Handler destructor.const RequestClient::Callback& request_callback =base::Bind(&Handler::OnRequestComplete, base::Unretained(this));// Create and start a new CefURLRequest associated with the frame, so// that it shares authentication with ClientHandler::GetAuthCredentials.urlrequest_ = frame->CreateURLRequest(cef_request, new RequestClient(request_callback));return true;}return false;}private:// Cancel the currently pending URL request, if any.void CancelPendingRequest() {CEF_REQUIRE_UI_THREAD();if (urlrequest_.get()) {// Don't execute the callback when we explicitly cancel the request.static_cast<RequestClient*>(urlrequest_->GetClient().get())->Detach();urlrequest_->Cancel();urlrequest_ = nullptr;}if (callback_.get()) {// Must always execute |callback_| before deleting it.callback_->Failure(ERR_ABORTED, test_runner::GetErrorString(ERR_ABORTED));callback_ = nullptr;}}void OnRequestComplete(CefURLRequest::ErrorCode error_code,const std::string& download_data) {CEF_REQUIRE_UI_THREAD();if (error_code == ERR_NONE)callback_->Success(download_data);elsecallback_->Failure(error_code, test_runner::GetErrorString(error_code));callback_ = nullptr;urlrequest_ = nullptr;}CefRefPtr<Callback> callback_;CefRefPtr<CefURLRequest> urlrequest_;DISALLOW_COPY_AND_ASSIGN(Handler);
};

OnQuery

这个函数就是响应html文件中的JS事件的响应函数。

  • const std::string& load_url = message_name.substr(sizeof(kTestMessageName)); 过滤掉filter的关键字,留下HTTP请求的url地址。

  • 创建http请求

    // Create a CefRequest for the specified URL.
    CefRefPtr<CefRequest> cef_request = CefRequest::Create();
    cef_request->SetURL(load_url);
    cef_request->SetMethod("GET");
    
  • 提交http请求

    urlrequest_ = frame->CreateURLRequest(cef_request, new RequestClient(request_callback));
    

    简单来说,就是把这个request请求提交到对应的服务端了。

  • 再创建一个callback(一个函数指针),这个callback不是cef框架到JS界面的callback,而是urlrequest类相关的,处理整个http请求各关键事件的callback。

    // Callback to be executed on request completion.
    // It's safe to use base::Unretained() here because there is only one
    // RequestClient pending at any given time and we explicitly detach the
    // callback in the Handler destructor.
    const RequestClient::Callback& request_callback =base::Bind(&Handler::OnRequestComplete, base::Unretained(this));
    
  • OnRequestComplete,也就是当requst请求得到响应后,CEF框架就会调用这个函数,在上面的代码中可以看到,就是调用了callback_方法(这个callback就是记录了JS的匿名方法了),把对应的download_data返回到前端显示。

CefURLRequestClient

前面提到的都是从JS到CEF框架,然后再到消息路由,最后到某种消息的处理HANDLE,而这个消息HANDLE中最后才调用到整个URLREQUEST的框架HANDLE:CefURLRequestClient,定义在cef_urlrequest.h中。

class CefURLRequestClient : public virtual CefBaseRefCounted {public:virtual void OnRequestComplete(CefRefPtr<CefURLRequest> request) = 0;virtual void OnUploadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total) = 0;virtual void OnDownloadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total) = 0;virtual void OnDownloadData(CefRefPtr<CefURLRequest> request,const void* data,size_t data_length) = 0;virtual bool GetAuthCredentials(bool isProxy,const CefString& host,int port,const CefString& realm,const CefString& scheme,CefRefPtr<CefAuthCallback> callback) = 0;
};

为了节省篇幅,我将这个类中的所有注释全部去掉了,这个类就是定义了在HTTP请求的整个过程中,几个关键事件的钩子函数,这个几个函数都是纯虚函数,所以需要完成URLREQUEST的使用的话,自定义一个类对这个几个函数都需要重定义。

在CEF的示例中,代码为:

class RequestClient : public CefURLRequestClient {public:// Callback to be executed on request completion.typedef base::Callback<void(CefURLRequest::ErrorCode /*error_code*/,const std::string& /*download_data*/)>Callback;explicit RequestClient(const Callback& callback) : callback_(callback) {CEF_REQUIRE_UI_THREAD();DCHECK(!callback_.is_null());}void Detach() {CEF_REQUIRE_UI_THREAD();if (!callback_.is_null())callback_.Reset();}void OnRequestComplete(CefRefPtr<CefURLRequest> request) OVERRIDE {CEF_REQUIRE_UI_THREAD();if (!callback_.is_null()) {callback_.Run(request->GetRequestError(), download_data_);callback_.Reset();}}void OnUploadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total) OVERRIDE {}void OnDownloadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total) OVERRIDE {}void OnDownloadData(CefRefPtr<CefURLRequest> request,const void* data,size_t data_length) OVERRIDE {CEF_REQUIRE_UI_THREAD();download_data_ += std::string(static_cast<const char*>(data), data_length);std::cout << download_data_ << std::endl;}bool GetAuthCredentials(bool isProxy,const CefString& host,int port,const CefString& realm,const CefString& scheme,CefRefPtr<CefAuthCallback> callback) OVERRIDE {return false;}private:Callback callback_;std::string download_data_;IMPLEMENT_REFCOUNTING(RequestClient);DISALLOW_COPY_AND_ASSIGN(RequestClient);
};

几个纯虚函数的具体作用,可以参考cef_urlrequest.h中的注释,而且从名字也很容易判断出来,也就是CEF框架对这种通过的URL请求提供了这些自定义能力。

在这个示例中,主要就是通过定义OnDownloadData方法,来将所有的下载到的内容放到download_data_字符串中(CEF每次下载的大小由trunk大小来控制)。

这篇关于CEF框架:各种各样的Handle(四)——CefURLRequest,发起HTTP请求与处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1009232

相关文章

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

Golang 日志处理和正则处理的操作方法

《Golang日志处理和正则处理的操作方法》:本文主要介绍Golang日志处理和正则处理的操作方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录1、logx日志处理1.1、logx简介1.2、日志初始化与配置1.3、常用方法1.4、配合defer

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

Python处理大量Excel文件的十个技巧分享

《Python处理大量Excel文件的十个技巧分享》每天被大量Excel文件折磨的你看过来!这是一份Python程序员整理的实用技巧,不说废话,直接上干货,文章通过代码示例讲解的非常详细,需要的朋友可... 目录一、批量读取多个Excel文件二、选择性读取工作表和列三、自动调整格式和样式四、智能数据清洗五、

SpringBoot如何对密码等敏感信息进行脱敏处理

《SpringBoot如何对密码等敏感信息进行脱敏处理》这篇文章主要为大家详细介绍了SpringBoot对密码等敏感信息进行脱敏处理的几个常用方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录​1. 配置文件敏感信息脱敏​​2. 日志脱敏​​3. API响应脱敏​​4. 其他注意事项​​总结

Python使用python-docx实现自动化处理Word文档

《Python使用python-docx实现自动化处理Word文档》这篇文章主要为大家展示了Python如何通过代码实现段落样式复制,HTML表格转Word表格以及动态生成可定制化模板的功能,感兴趣的... 目录一、引言二、核心功能模块解析1. 段落样式与图片复制2. html表格转Word表格3. 模板生