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

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.cpp
  window.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);
    else
      callback_->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大小来控制)。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新兴AI民工

码字不易,各位看客随意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值