使用CEF类库处理HTTP请求
当我们基于CEF开发应用时,可能会有URL请求处理的需求,比方HTTP下载或上传。此时能够利用CEF提供的类库来完毕,而不必自己实现或引入其他第三方的类库。
在CEF里为URL Request设计了两组类,一组用于运行网络请求,一组代表请求数据。
foruok原创。转载请保留出处或关注foruok的微信订阅号“程序视界”来联系foruok。
URLRequest
CefURLRequest是运行URL请求的类(接口),相应的头文件是cef_urlrequest.h,实现则在libcef/common/urlrequest_impl.cc文件里。
CefURLRequest类的静态方法Create()能够创建并运行一个URL请求。它的原型例如以下:
static CefRefPtr<CefURLRequest> Create(
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context);
第一个參数,类型是CefRequest,代表一个URL请求。CEF库内部已经实现了,后面会讲到。
第二个參数。类型是CefURLRequestClient,用于接收server返回的状态和数据,须要我们自己继承CefURLRequestClient接口实现一个非抽象类。
后面有了。
第三个參数。CefRequestContext,为NULL时内部会自己创建一个合适的Context。不为NULL时就用传入的Context。
Create方法会依据当前是Browser进程还是Renderer进程来创建相应的URLRequest类,CefBrowserURLRequest(browser_urlrequest_impl.h/.cc)或CefRenderURLRequest(render_urlrequest_impl.h/.cc)。
这么分析下来,我们要进行URL请求,实际上要做的工作就是:
- 构造一个CefRequest,代表我们的请求
- 写一个类实现CefURLRequestClient接口来处理响应。
- 调用CefURLRequest::Create()创建一个URL请求处理对象
构造Request
CefRequest类代表了一个URL请求。它里面能够配置方法、URL、头部、上传的数据等。以下的代码片段演示了怎样构造一个 CefRequest 对象:
CefRefPtr<CefPostData> data = CefPostData::Create();
CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
const char szData[] = "Hello World!";
element->SetToBytes(sizeof(szData) - 1, (const void*)szData);
data->AddElement(element);
CefRequest::HeaderMap headers;
headers.insert(std::make_pair("Content-Type", "text/plain"));
headers.insert(std::make_pair("Accept", "text/plain"));
CefRefPtr<CefRequest> req = CefRequest::Create();
req->SetMethod("POST");
req->SetURL("http://xxx.net");
req->SetHeaderMap(headers);
req->SetPostData(data);
与一个请求相关的类和接口,都在cef_request.h中,实如今request_impl.cc中。
这些类都有静态的Create方法。能够返回一个代表详细实例的接口。然后就能够接口的方法来定制实例对象,定制后的对象就能够用于URL请求了。
刚才的代码片段演示了怎样构造一个CefRequest对象。当中用到了以下的类(接口):
- CefRequest,代表了一个URL请求
- CefPostData,管理要通过请求发送的数据。它内部维护了多个CefPostDataElement。每一个CefPostDataElement代表了一个要发送的数据元素
- CefPostDataElement,代表发送的数据。提供了一些接口。能够关联到文件,也能够直接发送字节
想了解至于这些类的接口,打开头文件看看吧。
实现CefURLRequestClient接口
CefURLRequestClient接口的实现能够非常easy。我实现了一个简单的UrlRequestClient类。
UrlRequestClient.h例如以下:
#ifndef URL_REQUEST_CLIENT_H
#define URL_REQUEST_CLIENT_H
#include <string>
#include "include/cef_urlrequest.h"
#include "include/wrapper/cef_helpers.h"
class UrlRequestCompletionCallback
{
public:
virtual ~UrlRequestCompletionCallback(){}
virtual void OnCompletion(CefURLRequest::ErrorCode errorCode,
const std::string& data) = 0;
};
class UrlRequestClient : public CefURLRequestClient
{
public:
UrlRequestClient()
: m_callback(0)
{
CEF_REQUIRE_UI_THREAD();
}
UrlRequestClient(UrlRequestCompletionCallback *callback)
: m_callback(callback)
{
CEF_REQUIRE_UI_THREAD();
}
//
//interfaces of CefURLRequestClient
//
void OnRequestComplete(CefRefPtr<CefURLRequest> request) OVERRIDE;
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;
bool GetAuthCredentials(bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
CefRefPtr<CefAuthCallback> callback) OVERRIDE{
return false;
}
void Request(CefRefPtr<CefRequest> cef_request);
void Get(const std::string &url, const CefRequest::HeaderMap &headers = CefRequest::HeaderMap());
void Post(const std::string &url, const CefRefPtr<CefPostData> data, const CefRequest::HeaderMap &headers = CefRequest::HeaderMap());
void SetCompletionCallback(UrlRequestCompletionCallback *callback)
{
m_callback = callback;
}
private:
UrlRequestCompletionCallback *m_callback;
CefRefPtr<CefURLRequest> m_urlRequest;
std::string m_data;
IMPLEMENT_REFCOUNTING(UrlRequestClient);
DISALLOW_COPY_AND_ASSIGN(UrlRequestClient);
};
class PrintUrlReqCallback : public UrlRequestCompletionCallback
{
public:
void OnCompletion(CefURLRequest::ErrorCode errorCode, const std::string& data);
};
#endif
UrlRequestClient.cpp内容例如以下:
#include "UrlRequestClient.h"
#include <Windows.h>
void UrlRequestClient::OnRequestComplete(CefRefPtr<CefURLRequest> request)
{
CEF_REQUIRE_UI_THREAD();
if (m_callback) {
m_callback->OnCompletion(request->GetRequestError(), m_data);
}
}
void UrlRequestClient::OnUploadProgress(CefRefPtr<CefURLRequest> request, int64 current, int64 total)
{
}
void UrlRequestClient::OnDownloadProgress(CefRefPtr<CefURLRequest> request, int64 current, int64 total)
{
char szLog[128] = { 0 };
sprintf_s(szLog, 128, "UrlRequestClient::OnDownloadProgress, current-%lld, total-%lld\r\n",
current, total);
OutputDebugStringA(szLog);
}
void UrlRequestClient::OnDownloadData(CefRefPtr<CefURLRequest> request, const void* data, size_t data_length)
{
m_data += std::string(static_cast<const char*>(data), data_length);
}
void UrlRequestClient::Request(CefRefPtr<CefRequest> cef_request)
{
m_urlRequest = CefURLRequest::Create(cef_request, this, NULL);
}
void UrlRequestClient::Get(const std::string &url, const CefRequest::HeaderMap &headers /*= CefRequest::HeaderMap()*/)
{
CefRefPtr<CefRequest> req = CefRequest::Create();
req->SetURL(url);
req->SetMethod("GET");
req->SetHeaderMap(headers);
Request(req);
}
void UrlRequestClient::Post(const std::string &url, const CefRefPtr<CefPostData> data, const CefRequest::HeaderMap &headers /*= CefRequest::HeaderMap()*/)
{
CefRefPtr<CefRequest> req = CefRequest::Create();
req->SetURL(url);
req->SetMethod("POST");
req->SetHeaderMap(headers);
req->SetPostData(data);
Request(req);
}
//
// for test
//
void PrintUrlReqCallback::OnCompletion(CefURLRequest::ErrorCode errorCode, const std::string& data)
{
char szLog[128] = { 0 };
sprintf_s(szLog, 128, "PrintUrlReqCallback::OnCompletion, errorCode = %d, data.len = %d, data:\r\n",
errorCode, data.length());
OutputDebugStringA(szLog);
delete this;
}
UrlRequestClient类能够发起URL请求并处理响应。它的使用方法相似以下这样(注意要在Browser进程的UI线程使用):
// Get() test
UrlRequestClient *client = new UrlRequestClient(new PrintUrlReqCallback);
std::string url("http://www.baidu.com");
client->Get(url);
// Request() test
CefRefPtr<CefRequest> req = CefRequest::Create();
req->SetMethod("GET");
req->SetURL("http://www.csdn.net");
CefRequest::HeaderMap headers;
headers.insert(std::make_pair("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"));
headers.insert(std::make_pair("Accept-Encoding", "gzip,deflate,sdch"));
headers.insert(std::make_pair("Accept-Language", "en,zh"));
req->SetHeaderMap(headers);
(new UrlRequestClient(new PrintUrlReqCallback))->Request(req);
// Post() test
CefRefPtr<CefPostData> data = CefPostData::Create();
CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
const char szData[] = "Hello World!";
element->SetToBytes(sizeof(szData) - 1, (const void*)szData);
data->AddElement(element);
CefRequest::HeaderMap headers;
headers.insert(std::make_pair("Content-Type", "text/plain"));
headers.insert(std::make_pair("Accept", "text/plain"));
(new UrlRequestClient(new PrintUrlReqCallback))->Post("http://xxx.com/hello", data, headers);
就这样吧。
关于CEF里的线程,能够參考:http://blog.csdn.net/foruok/article/details/50674141。
关于进程,能够參考:http://blog.csdn.net/foruok/article/details/50621751。
其他參考文章详见我的专栏:【CEF与PPAPI开发】。