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

};
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值