windows C++-使用任务和 XML HTTP 请求进行连接(一)

本文会演示如何将 IXMLHTTPRequest2 和 IXMLHTTPRequest2Callback 接口与任务结合使用,以将 HTTP GET 和 POST 请求发送至通用 Windows 平台 (UWP) 应用中的 Web 服务。 通过将 IXMLHTTPRequest2 与任务组合在一起,你可以编写通过其他任务编写的代码。 例如,可以使用下载任务作为任务链的一部分。 工作取消时,下载任务也会响应。

还可以使用 C++ REST SDK 从使用 C++ 应用的 UWP 应用或从桌面 C++ 应用来执行 HTTP 请求。

本文档首先演示如何创建 HttpRequest 及其支持类。 然后,演示如何从使用 C++ 和 XAML 的 UWP 应用使用此类。

IXMLHTTPRequest2 和 IXMLHTTPRequest2Callback 是建议在 UWP 应用中使用的接口。 还可以调整此示例,以用于桌面应用程序。

先决条件

Visual Studio 2017 及更高版本中的 UWP 支持是可选的。 若要安装它,请从 Windows"开始"菜单打开 Visual Studio 安装程序,并选择正在使用的 Visual Studio 版本。 单击"修改"按钮,确保选中"UWP 开发"磁贴。 在"可选组件"下,确保选中"C++ UWP 工具"。 对于 Visual Studio 2017,请使用 v141;对于 Visual Studio 2019,请使用 v142。

定义 HttpRequest、HttpRequestBuffersCallback 和 HttpRequestStringCallback 类

当您使用 IXMLHTTPRequest2 接口通过 HTTP 创建 Web 请求时,可以实现 IXMLHTTPRequest2Callback 接口来接收服务器响应并对其他事件做出响应。 此示例定义了 HttpRequest 类来创建 Web 请求,并定义了 HttpRequestBuffersCallback 和 HttpRequestStringCallback 类来处理响应。 HttpRequestBuffersCallback 和 HttpRequestStringCallback 类支持 HttpRequest 类;在应用程序代码中您只能使用 HttpRequest 类。

GetAsync 类的 PostAsync 和 HttpRequest 方法可以使您分别启动 HTTP GET 和 POST 操作。 这些方法使用 HttpRequestStringCallback 类以便将服务器响应作为字符串读取。 SendAsync 和 ReadAsync 方法使您能够对区块中的大型内容进行流式处理。 这些方法均返回 concurrency::task 以表示操作。 GetAsync 和 PostAsync 方法会产生 task<std::wstring> 值,其中 wstring 部分表示服务器的响应。 SendAsync 和 ReadAsync 方法将产生 task<void> 值;当发送和读取操作完成后这些任务将会完成。

因为 IXMLHTTPRequest2 接口是异步操作的,本示例使用 concurrency::task_completion_event 来创建一个任务,该任务将在回调对象完成或取消下载操作后完成。 HttpRequest 类从此任务创建基于任务的延续以设置最终结果。 HttpRequest 类使用基于任务的延续来确保,即使在前面的任务产生错误或取消的情况下,延续任务也会运行。 有关基于任务的延续的详细信息,请参阅任务并行

若要支持取消,HttpRequest、HttpRequestBuffersCallback 和 HttpRequestStringCallback 类将使用取消标记。 HttpRequestBuffersCallback 和 HttpRequestStringCallback 类使用 concurrency::cancellation_token::register_callback 方法使任务完成事件能够响应取消。

定义 HttpRequest 类
  1. 在主菜单中,选择"文件">"新建">"项目"。

  2. 使用 C++"空白应用(通用 Windows)"模板来创建空白 XAML 应用项目。 此示例将项目命名为 UsingIXMLHTTPRequest2。

  3. 在项目中添加一个名为 HttpRequest.h 的标头文件和一个名为 HttpRequest.cpp 的源文件。

  4. 在 pch.h 中,添加此代码:

    #include <ppltasks.h>
    #include <string>
    #include <sstream>
    #include <wrl.h>
    #include <msxml6.h>

  5. 在 HttpRequest.h 中,添加此代码:

我们会创建两个类,分别应对不同的情况,第一个类定义如下:

复制代码
#pragma once
#include "pch.h"

inline void CheckHResult(HRESULT hResult)
{
    if (hResult == E_ABORT)
    {
        concurrency::cancel_current_task();
    }
    else if (FAILED(hResult))
    {
        throw Platform::Exception::CreateException(hResult);
    }
}

namespace Web
{

namespace Details
{

// 当响应需要部分缓冲区时,使用IXMLHTTPRequest2Callback的实现。
// 当只需要完整的响应时,请改用HttpRequestStringCallback。
class HttpRequestBuffersCallback 
    : public Microsoft::WRL::RuntimeClass<
        Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
        IXMLHTTPRequest2Callback,
        Microsoft::WRL::FtmBase>
{
public:
    HttpRequestBuffersCallback(IXMLHTTPRequest2* httpRequest, 
        concurrency::cancellation_token ct = concurrency::cancellation_token::none()) :
        request(httpRequest), cancellationToken(ct), responseReceived(false), dataHResult(S_OK), statusCode(200)
    {
        // Register a callback function that aborts the HTTP operation when 
        // the cancellation token is canceled.
        if (cancellationToken != concurrency::cancellation_token::none())
        {
            registrationToken = cancellationToken.register_callback([this]() 
            {
                if (request != nullptr) 
                {
                    request->Abort();
                }
            });
        }

        dataEvent = concurrency::task_completion_event<void>();
    }

    // Called when the HTTP request is being redirected to a new URL.
    IFACEMETHODIMP OnRedirect(IXMLHTTPRequest2*, PCWSTR) 
    {
        return S_OK;
    }

    // Called when HTTP headers have been received and processed.
    IFACEMETHODIMP OnHeadersAvailable(IXMLHTTPRequest2*, DWORD statusCode, PCWSTR reasonPhrase)
    {
        HRESULT hr = S_OK;

        // We must not propagate exceptions back to IXHR2.
        try
        {
            this->statusCode = statusCode;
            this->reasonPhrase = reasonPhrase;

            concurrency::critical_section::scoped_lock lock(dataEventLock);
            dataEvent.set();
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }
        return hr;
    }

    // Called when a portion of the entity body has been received.
    IFACEMETHODIMP OnDataAvailable(IXMLHTTPRequest2*, ISequentialStream* stream)
    {
        HRESULT hr = S_OK;

        // We must not propagate exceptions back to IXHR2.
        try
        {
            // Store a reference on the stream so it can be accessed by the task.
            dataStream = stream;

            // The work must be done as fast as possible, and must not block this thread,
            // for example, waiting on another event object.  Here we simply set an event
            // that can be processed by another thread.
            concurrency::critical_section::scoped_lock lock(dataEventLock);
            dataEvent.set();
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }
        return hr;
    }
        
    // Called when the entire entity response has been received.
    IFACEMETHODIMP OnResponseReceived(IXMLHTTPRequest2* xhr, ISequentialStream* responseStream)
    {
        responseReceived = true;
        return OnDataAvailable(xhr, responseStream);
    }
        
    // Called when an error occurs during the HTTP request.
    IFACEMETHODIMP OnError(IXMLHTTPRequest2*, HRESULT hrError) 
    {
        HRESULT hr = S_OK;

        // We must not propagate exceptions back to IXHR2.
        try
        {
            concurrency::critical_section::scoped_lock lock(dataEventLock);
            dataHResult = hrError;
            dataEvent.set();
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }

        return hr;
    }

    // Create a task that completes when data is available, in an exception-safe way.
    concurrency::task<void> CreateDataTask();

    HRESULT GetError() const
    {
        return dataHResult;
    }

    int GetStatusCode() const
    {
        return statusCode;
    }

    std::wstring const& GetReasonPhrase() const
    {
        return reasonPhrase;
    }

    bool IsResponseReceived() const
    {
        return responseReceived;
    }

    // Copy bytes from the sequential stream into the buffer provided until
    // we reach the end of one or the other.
    unsigned int ReadData(
        _Out_writes_(outputBufferSize) byte* outputBuffer,
        unsigned int outputBufferSize);

private:
    ~HttpRequestBuffersCallback()
    {
        // Unregister the callback.
        if (cancellationToken != concurrency::cancellation_token::none())
        {
            cancellationToken.deregister_callback(registrationToken);
        }
    }

    // Signals that the download operation was canceled.
    concurrency::cancellation_token cancellationToken;

    // Used to unregister the cancellation token callback.
    concurrency::cancellation_token_registration registrationToken;

    // The IXMLHTTPRequest2 that processes the HTTP request.
    Microsoft::WRL::ComPtr<IXMLHTTPRequest2> request;
    
    // Task completion event that is set when data is available or error is triggered.
    concurrency::task_completion_event<void> dataEvent;
    concurrency::critical_section dataEventLock;

    // We cannot store the error obtained from IXHR2 in the dataEvent since any value there is first-writer-wins,
    // whereas we want a subsequent error to override an initial success.
    HRESULT dataHResult;

    // Referenced pointer to the data stream.
    Microsoft::WRL::ComPtr<ISequentialStream> dataStream;

    // HTTP status code and reason returned by the server.
    int statusCode;
    std::wstring reasonPhrase;

    // Whether the response has been completely received.
    bool responseReceived;
};

};
};
相关推荐
姜君竹21 分钟前
QT的工程文件.pro文件
开发语言·c++·qt·系统架构
思捻如枫23 分钟前
C++数据结构和算法代码模板总结——算法部分
数据结构·c++
weixin_478689761 小时前
C++ 对 C 的兼容性
java·c语言·c++
k要开心1 小时前
C++概念以及基础框架语法
开发语言·c++
夏日米米茶1 小时前
Windows系统下npm报错node-gyp configure got “gyp ERR“解决方法
前端·windows·npm
weixin_307779132 小时前
Linux下GCC和C++实现统计Clickhouse数据仓库指定表中各字段的空值、空字符串或零值比例
linux·运维·c++·数据仓库·clickhouse
秦少游在淮海3 小时前
C++ - string 的使用 #auto #范围for #访问及遍历操作 #容量操作 #修改操作 #其他操作 #非成员函数
开发语言·c++·stl·string·范围for·auto·string 的使用
const5443 小时前
cpp自学 day2(—>运算符)
开发语言·c++
虾球xz3 小时前
CppCon 2015 学习:CLANG/C2 for Windows
开发语言·c++·windows·学习
码上库利南3 小时前
Windows开机自动启动中间件
windows