65.9K
CodeProject 正在变化。 阅读更多。
Home

CHttpClient - 使用 WinInet 的辅助类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (56投票s)

2004 年 7 月 29 日

3分钟阅读

viewsIcon

521333

downloadIcon

25036

一个 C++ 类,帮助您与 HTTP Web 服务器进行交互。

Screenshot - lyoulhttpclient.jpg

引言

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 () ;
} ;

在抛出异常之前,大多数方法会恢复其内部状态(类似事务的“全有或全无”)。但是,如果您调用 BeginPostBeginUpload 方法,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) ;
    }
}

如何向用户显示进度信息

如果您调用 BeginPostBeginUpload 方法,可以使用 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 日
    • 初始发布。
© . All rights reserved.