CHttpClient - 使用 WinInet 的辅助类






4.96/5 (56投票s)
2004 年 7 月 29 日
3分钟阅读

521333

25036
一个 C++ 类,帮助您与 HTTP Web 服务器进行交互。
- 下载 CHttpClient 源代码文件 - 72.9 KB
- 下载 CHttpClient 演示项目 - 109.9 KB
- 下载 CHttpClient 帮助文件 - 445.9 KB
- 下载 CHttpClient COM 组件版源代码文件 - 141.6 KB
- 下载 CHttpClient COM 组件版演示项目 - 55.4 KB
- 下载 CHttpClient COM 组件版帮助文件 - 218.3 KB
- 下载 CHttpClient COM 组件版编译后的二进制文件 - 77.9 KB

引言
CHttpClient
是一个使用 WinInet API 的辅助类。该类的目的是帮助您与 HTTP Web 服务器进行交互。该类的设计目标如下:
- 易于使用。
- 尽可能多的灵活性。
- 严格的错误处理。
我将简要介绍如何使用此类。所有详细描述已记录在附加的帮助文件中。
如何使用 CHttpClient
在您的项目中,包含以下文件
- RyeolException.h
- RyeolException.cpp
- RyeolHttpClient.h
- RyeolHttpClient.cpp
- SafeInt.hpp
在您的 stdafx.h 文件中,添加以下行:
#include "RyeolHttpClient.h"
注意:命名空间和文件名已更改。如果您使用的是旧版本的 CHttpClient
,则需要更新如下:
- LyoulException.h --> RyeolException.h
- LyoulException.cpp --> RyeolException.cpp
- LyoulHttpClient.h --> RyeolHttpClient.h
- LyoulHttpClient.cpp --> RyeolHttpClient.cpp
- #include "LyoulHttpClient.h" --> #include "RyeolHttpClient.h"
- using namespace Lyoul --> using namespace Ryeol
如何使用 HTTP GET 发送请求
CHttpClient
支持 RequestGet
方法,该方法使用 HTTP GET 发送请求。
// Retrieves the resource specified by the szUrl using HTTP GET request.
// szUrl [in] A HTTP URL.
// bUseCache [in] Specifies whether to use cache.
CHttpResponse * CHttpClient::RequestGet (PCSZ szUrl,
BOOL bUseCache = FALSE) throw (Exception &) ;
以下代码演示了 RequestGet
方法的基本用法:
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Specifies whether to use UTF-8 encoding.
// (This uses ANSI encoding)
// Default is FALSE
objHttpReq.SetUseUtf8 (FALSE) ;
// Specifies a code page for ANSI strings.
// (This uses Korean)
// Default is CP_ACP
objHttpReq.SetAnsiCodePage (949) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameters
objHttpReq.AddParam (_T ("where"), _T ("nexearch")) ;
objHttpReq.AddParam (_T ("frm"), _T ("t1")) ;
objHttpReq.AddParam (_T ("query"),
_T ("%C3%D6%C1%F6%BF%EC"), CHttpClient::ParamEncodedValue) ;
// Send a request
pobjHttpRes =
objHttpReq.RequestGet (_T ("http://search.naver.com/search.naver")) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
如何使用 HTTP POST 发送请求
HTTP POST 方法有两种用法。一种是发布纯文本,另一种是上传文件。要发布纯文本,CHttpClient
提供了 BeginPost
方法。
// Starts a new HTTP POST request
// szUrl [in] A HTTP URL.
// bUseCache [in] Specifies whether to use cache.
void CHttpClient::BeginPost (PCSZ szUrl,
BOOL bUseCache = FALSE) throw (Exception &) ;
以下代码演示了 BeginPost
方法的基本用法:
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameters
objHttpReq.AddParam (_T ("st"), _T ("kw")) ;
objHttpReq.AddParam (_T ("target"), _T ("WinInet")) ;
// Start a new request
objHttpReq.BeginPost (_T ("https://codeproject.org.cn/info/search.asp")) ;
// Specifies the number of bytes to send when the Proceed method is called.
const DWORD cbProceed = 1024 ; // 1K
do {
... // Place codes to report progress information to user.
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
要上传文件,CHttpClient
提供了 BeginUpload
方法。
// Starts a new UPLOAD request // szUrl [in] A HTTP URL. // bUseCache [in] Specifies whether to use cache. void CHttpClient::BeginUpload (PCSZ szUrl, BOOL bUseCache = FALSE) throw (Exception &) ;
以下代码演示了 BeginUpload
方法的基本用法:
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameters
objHttpReq.AddParam (_T ("nohtml"), _T ("1")) ;
objHttpReq.AddParam (_T ("title"), _T ("The K-NET photo")) ;
objHttpReq.AddParam (_T ("content"), _T ("A photo of the K-NET")) ;
// Specifies a file to upload
objHttpReq.AddParam (_T ("ufile01"),
_T ("D:\\My Photo\\K-NET\\photo1.jpg"),
CHttpClient::ParamFile) ;
// Start a new request
objHttpReq.BeginUpload (_T ("http://club.hooriza.com")
_T ("/cmd/box.html?clubid=1&boxid=53&action=store&link=")) ;
// Specifies the number of bytes to send when the Proceed method is called.
const DWORD cbProceed = 1024 ; // 1K
do {
... // Place codes to report progress information to user.
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception && e) {
... // Place exception handling codes here.
}
如何处理返回的 CHttpResponse 对象
当您使用 CHttpClient
发送请求时,所有方法都将返回 CHttpResponse
对象。CHttpResponse
表示 HTTP Web 服务器返回的响应。CHttpResponse
提供了以下方法:
// Returns the number of headers of which name is the szName
DWORD CHttpResponse::GetHeaderCount (PCSZ szName) throw (Exception &) ;
// Returns the header of which name is the szName
PCSZ CHttpResponse::GetHeader (PCSZ szName,
DWORD nIdx = 0) throw (Exception &) ;
// Returns the HTTP status code returned by a HTTP server
DWORD CHttpResponse::GetStatus (void) throw (Exception &) ;
// Returns the HTTP status text returned by a HTTP server
PCSZ CHttpResponse::GetStatusText (void) throw (Exception &) ;
// Retrieves the length of the content returned by a HTTP server
BOOL CHttpResponse::GetContentLength (DWORD & cbContLen) throw (Exception &) ;
// Reads the content returned by a HTTP server
DWORD CHttpResponse::ReadContent (BYTE * pbyBuff,
DWORD cbBuff) throw (Exception &) ;
以下代码演示了 CHttpResponse
对象的基本用法:
using namespace Ryeol ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Get the CHttpResponse object
pobjHttpRes = ... ;
// Reads the HTTP status code
_tprintf (_T ("%u"), pobjHttpRes->GetStatus ()) ;
// Reads the HTTP status text
_tprintf (_T (" %s\n"), pobjHttpRes->GetStatusText ()) ;
// Reads HTTP headers using an array of header names
static LPCTSTR szHeaders[] =
{ _T ("Server"), _T ("Date"), _T ("X-Powered-By"),
_T ("Content-Length"), _T ("Set-Cookie")
, _T ("Expires"), _T ("Cache-control"),
_T ("Connection"), _T ("Transfer-Encoding")
, _T ("Content-Type") } ;
LPCTSTR szHeader ;
for (size_t i = 0; i < sizeof (szHeaders) / sizeof (LPCTSTR); i++) {
if ( szHeader = pobjHttpRes->GetHeader (szHeaders[i]) )
_tprintf (_T ("%s: %s\n"), szHeaders[i], szHeader) ;
else
// If the header is not found..
_tprintf (_T ("'%s' header does not exist..\n"),
szHeaders[i]) ;
}
_tprintf (_T ("\n")) ;
// Checks whether the returned stream is a text
BOOL bIsText = FALSE ;
if ( szHeader = pobjHttpRes->GetHeader (_T ("Content-Type")) )
bIsText = (0 == ::_tcsncicmp (szHeader, _T ("text/"), 5)) ;
// Reads the length of the stream
DWORD dwContSize ;
// If the length is not specified
if ( !pobjHttpRes->GetContentLength (dwContSize) )
dwContSize = 0 ;
const DWORD cbBuff = 1024 * 10 ;
BYTE byBuff[cbBuff] ;
DWORD dwRead ;
size_t cbTotal = 0 ;
// Reads the data stream returned by the HTTP server.
while ( dwRead = pobjHttpRes->ReadContent (byBuff, cbBuff - 1) ) {
cbTotal += dwRead ;
if ( bIsText ) {
byBuff[dwRead] = '\0' ;
printf ("%s", reinterpret_cast<LPCSTR> (byBuff)) ;
}
}
if ( !bIsText )
_tprintf (_T ("%u bytes skipped..\n"), cbTotal) ;
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
delete pobjHttpRes ;
pobjHttpRes = NULL ;
如何处理异常
当发生错误时,将抛出 httpclientexception
对象。
class httpclientexception {
public:
// Returns the last error code.
// The error codes is defined in RyeolHttpClient.h
DWORD LastError (void) const throw () ;
// Returns the last error message.
LPCTSTR errmsg (void) const throw () ;
// Returns the last win32 error code retrieved by
// using ::GetLastError when an error occurred.
DWORD Win32LastError (void) const throw () ;
} ;
在抛出异常之前,大多数方法会恢复其内部状态(类似事务的“全有或全无”)。但是,如果您调用 BeginPost
或 BeginUpload
方法,POST 上下文将自动取消。您应该编写以下 try
-catch
块来处理异常:
using namespace Ryeol ;
try {
... // Place codes which throw a httpclientexception exception
} catch (httpclientexception & e) {
_tprintf (_T ("An error has been occured\n")) ;
_tprintf (_T ("ErrCode: 0x%x\n"), e.LastError ()) ;
_tprintf (_T ("ErrMsg: %s\n"), e.errmsg ()) ;
if ( e.Win32LastError () != NO_ERROR ) {
TCHAR szErrMsg[512] ;
GetWinInetErrMsg (szErrMsg, 512, e.Win32LastError ()) ;
_tprintf (_T ("Win32ErrCode: 0x%x\n"), e.Win32LastError ()) ;
_tprintf (_T ("Win32ErrMsg: %s\n"), szErrMsg) ;
}
}
如何向用户显示进度信息
如果您调用 BeginPost
或 BeginUpload
方法,可以使用 Query
方法检索进度信息。
// Queries progress information of the current POST context
// objPostStat [out] A CHttpPostStat object.
void CHttpClient::Query (CHttpPostStat & objPostStat) throw () ;
CHttpPostStat
表示当前 POST 上下文的进度信息。以下代码演示了 CHttpPostStat
对象的基本用法:
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
size_t cbProceed = 1024 ; // 1k
try {
... ; // Intialize the CHttpClient object
// Starts a new POST request
objHttpReq.BeginPost (...) or objHttpReq.BeginUpload (...) ;
// Displays progress information
CHttpPostStat objPostStat ;
do {
// Retrieves progress information
objHttpReq.Query (objPostStat) ;
_tprintf (_T ("\nPost in progress... %2u/%2u\n")
, objPostStat.PostedCount () // The number of posted parameters
, objPostStat.TotalCount ()) ; // The total number of parameters
_tprintf (_T ("%s: %10u/%10u %10u/%10u %10u/%10u\n")
// The name of the current parameter
, objPostStat.CurrParam ()
// The number of posted bytes of the current parameter
, objPostStat.CurrParamPostedByte ()
// The total number of bytes of the current parameter
, objPostStat.CurrParamTotalByte ()
// The number of posted bytes of the request
, objPostStat.PostedByte ()
// The total number of bytes of the request
, objPostStat.TotalByte ()
// The actual number of posted bytes of the request
, objPostStat.ActualPostedByte ()
// The actual total number of bytes of the request
, objPostStat.ActualTotalByte ()) ;
// If the current parameter is a file parameter,
// displays the file path
if ( objPostStat.CurrParamIsFile () )
_tprintf (_T ("-->%s\n")
, objPostStat.CurrFile ()) ;
// Sends the number of bytes specified by cbProceed to the server
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... ; // Handles the returned CHttpResponse object
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
关于 CHttpClient COM 组件版
CHttpClient
COM 组件版是 CHttpClient
的组件版本。我在这里不解释如何使用它,因为它与 CHttpClient
类似。您可以参考附加的示例和 CHttpClient
COM 组件版的帮助文件。如果您想使用 CHttpClient
COM 组件版,您需要安装 RyeolHttpClient2.dll。因为它是一个 COM DLL,您必须使用 regsvr32.exe 程序来注册它。您可以将其安装在任何您想要的地方,只要您不分发它。但是,如果您要分发它,我建议将其安装在 %WINDOWS%System32 文件夹中,并且即使您的程序被卸载也不要将其删除。
历史
- 2006 年 2 月 3 日
- 断言行为已更改为在断言表达式失败时抛出异常。
- 使用 pragma warning (push) 和 (pop) 指令来恢复之前的警告状态。
- 已添加
SaveContent
方法。 - 命名空间和文件名已更改。
- 已添加 CHttpClient COM 组件版。
- 2004 年 8 月 9 日
- 已添加一些方法以支持代理授权。
- 2004 年 7 月 29 日
- 初始发布。