演练:使用任务和 XML HTTP 请求 (IXHR2) 进行连接
此示例演示如何使用任务发送 HTTP GET 和 POST 请求一起使用 IXMLHTTPRequest2 和 IXMLHTTPRequest2Callback 接口到 Windows 应用商店 app 的 web 服务。 通过将与任务一起 IXHR2,可以构成与其他任务的代码。 例如,作为任务的一部分,链可以使用下载任务。 工作的时间时,下载任务还可以响应。
有关任务的更多信息,请参见 任务并行(并发运行时)。 有关如何使用任务的更多信息。Windows 应用商店 app,请参见 Asynchronous programming in C++ 和 用 C++ 为 Windows 应用商店应用程序创建异步操作。
文档第一个演示如何创建 HttpRequest 及其支持的选件类。 然后演示如何使用 C++ 和 XAML 从 Windows 应用商店 app 的此选件类。
对于使用的更完整示例 HttpReader 选件类中描述的文档,请参见 在 JavaScript 和 C++ 中开发 Windows 应用商店应用程序 Bing 地图行程优化器。 有关使用 IXHR2,但不使用任务的其他示例,请参见 Quickstart: Connecting using XML HTTP Request (IXHR2)。
提示
IXMLHTTPRequest2 和 IXMLHTTPRequest2Callback 是建议用于 Windows 应用商店 app 的接口。还可以满足此示例用于桌面应用程序。
定义 HttpRequest、HttpRequestBuffersCallback 和 HttpRequestStringCallback 选件类
当您使用 IXMLHTTPRequest2 接口创建在 HTTP 时的 web 请求,则实现 IXMLHTTPRequest2Callback 接口接收服务器答复和响应到其他活动。 此示例定义 HttpRequest 选件类创建 web 请求和 HttpRequestBuffersCallback 和 HttpRequestStringCallback 选件类处理响应。 HttpRequestBuffersCallback 和 HttpRequestStringCallback 选件类支持 HttpRequest 选件类;您只能使用从应用程序代码的 HttpRequest 选件类一起使用。
GetAsync,HttpRequest 选件类的 PostAsync 方法可以启动 HTTP GET 和 POST 操作,分别。 这些方法使用 HttpRequestStringCallback 选件类读取服务器响应以字符串。 SendAsync 和 ReadAsync 方法使您能够以增加的流用内容。 上述每种方法返回 concurrency::task 表示运算。 GetAsync 和 PostAsync 方法产生 task<std::wstring> 值,wstring 部件表示该服务器的响应。 SendAsync 和 ReadAsync 方法产生 task<void> 值;这些任务完成,当完成发送和读取操作。
由于 IXHR2 接口操作以异步方式,此示例使用 concurrency::task_completion_event 创建完成的任务,在回调对象完成后或取消下载操作。 HttpRequest 选件类创建从此任务的基于任务的延续设置最终结果。 HttpRequest 选件类使用基于任务的延续确保延续任务运行,即使前面的任务会导致错误或取消。 有关基于任务的延续的更多信息,请参见" 任务并行(并发运行时)
若要支持取消,HttpRequest、HttpRequestBuffersCallback和 HttpRequestStringCallback 选件类使用取消标记。 HttpRequestBuffersCallback 和 HttpRequestStringCallback 选件类使用 concurrency::cancellation_token::register_callback 方法使任务完成事件时响应取消。 此移除回调中止下载。 有关取消操作的更多信息,请参见 PPL 中的取消操作。
定义 HttpRequest 选件类
使用 Visual C++ 空白应用程序 (XAML) 模板创建空白 XAML 应用程序项目。 此示例将项目命名为 UsingIXHR2。
向项目添加名为 HttpRequest.h 和源文件名为 HttpRequest.cpp 的一个标头文件。
在 pch.h,添加以下代码:
#include <ppltasks.h> #include <string> #include <sstream> #include <wrl.h> #include <msxml6.h>
在 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 { // Implementation of IXMLHTTPRequest2Callback used when partial buffers are needed from the response. // When only the complete response is needed, use HttpRequestStringCallback instead. 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; }; }; // Utility class for performing asynchronous HTTP requests. // This class only supports one outstanding request at a time. class HttpRequest { public: HttpRequest(); int GetStatusCode() const { return statusCode; } std::wstring const& GetReasonPhrase() const { return reasonPhrase; } // Whether the response has been completely received, if using ReadAsync(). bool IsResponseComplete() const { return responseComplete; } // Start an HTTP GET on the specified URI. The returned task completes once the entire response // has been received, and the task produces the HTTP response text. The status code and reason // can be read with GetStatusCode() and GetReasonPhrase(). concurrency::task<std::wstring> GetAsync( Windows::Foundation::Uri^ uri, concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none()); // Start an HTTP POST on the specified URI, using a string body. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). concurrency::task<std::wstring> PostAsync( Windows::Foundation::Uri^ uri, PCWSTR contentType, IStream* postStream, uint64 postStreamSizeToSend, concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none()); // Start an HTTP POST on the specified URI, using a stream body. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). concurrency::task<std::wstring> PostAsync( Windows::Foundation::Uri^ uri, const std::wstring& str, concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none()); // Send a request but don't return the response. Instead, let the caller read it with ReadAsync(). concurrency::task<void> SendAsync( const std::wstring& httpMethod, Windows::Foundation::Uri^ uri, concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none()); // Read a chunk of data from the HTTP response, up to a specified length or until we reach the end // of the response, and store the value in the provided buffer. This is useful for large content, // enabling the streaming of the result. concurrency::task<void> ReadAsync( Windows::Storage::Streams::IBuffer^ readBuffer, unsigned int offsetInBuffer, unsigned int requestedBytesToRead); static void CreateMemoryStream(IStream **stream); private: // Start a download of the specified URI using the specified method. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). concurrency::task<std::wstring> DownloadAsync( PCWSTR httpMethod, PCWSTR uri, concurrency::cancellation_token cancellationToken, PCWSTR contentType, IStream* postStream, uint64 postStreamBytesToSend); // Referenced pointer to the callback, if using SendAsync/ReadAsync. Microsoft::WRL::ComPtr<Details::HttpRequestBuffersCallback> buffersCallback; int statusCode; std::wstring reasonPhrase; // Whether the response has been completely received, if using ReadAsync(). bool responseComplete; }; };
在 HttpRequest.cpp,添加以下代码:
#include "pch.h" #include "HttpRequest.h" #include <robuffer.h> #include <shcore.h> using namespace concurrency; using namespace Microsoft::WRL; using namespace Platform; using namespace std; using namespace Web; using namespace Windows::Foundation; using namespace Windows::Storage::Streams; // Implementation of IXMLHTTPRequest2Callback used when only the complete response is needed. // When processing chunks of response data as they are received, use HttpRequestBuffersCallback instead. class HttpRequestStringCallback : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IXMLHTTPRequest2Callback, FtmBase> { public: HttpRequestStringCallback(IXMLHTTPRequest2* httpRequest, cancellation_token ct = concurrency::cancellation_token::none()) : request(httpRequest), cancellationToken(ct) { // Register a callback function that aborts the HTTP operation when // the cancellation token is canceled. if (cancellationToken != cancellation_token::none()) { registrationToken = cancellationToken.register_callback([this]() { if (request != nullptr) { request->Abort(); } }); } } // 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; } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } return hr; } // Called when a portion of the entity body has been received. IFACEMETHODIMP OnDataAvailable(IXMLHTTPRequest2*, ISequentialStream*) { return S_OK; } // Called when the entire entity response has been received. IFACEMETHODIMP OnResponseReceived(IXMLHTTPRequest2*, ISequentialStream* responseStream) { wstring wstr; HRESULT hr = ReadUtf8StringFromSequentialStream(responseStream, wstr); // We must not propagate exceptions back to IXHR2. try { completionEvent.set(make_tuple<HRESULT, wstring>(move(hr), move(wstr))); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } return hr; } // Simulate the functionality of DataReader.ReadString(). // This is needed because DataReader requires IRandomAccessStream and this // code has an ISequentialStream that does not have a conversion to IRandomAccessStream like IStream does. HRESULT ReadUtf8StringFromSequentialStream(ISequentialStream* readStream, wstring& str) { // Convert the response to Unicode wstring. HRESULT hr; // Holds the response as a Unicode string. wstringstream ss; while (true) { ULONG cb; char buffer[4096]; // Read the response as a UTF-8 string. Since UTF-8 characters are 1-4 bytes long, // we need to make sure we only read an integral number of characters. So we'll // start with 4093 bytes. hr = readStream->Read(buffer, sizeof(buffer) - 3, &cb); if (FAILED(hr) || (cb == 0)) { break; // Error or no more data to process, exit loop. } if (cb == sizeof(buffer) - 3) { ULONG subsequentBytesRead; unsigned int i, cl; // Find the first byte of the last UTF-8 character in the buffer. for (i = cb - 1; (i >= 0) && ((buffer[i] & 0xC0) == 0x80); i--); // Calculate the number of subsequent bytes in the UTF-8 character. if (((unsigned char)buffer[i]) < 0x80) { cl = 1; } else if (((unsigned char)buffer[i]) < 0xE0) { cl = 2; } else if (((unsigned char)buffer[i]) < 0xF0) { cl = 3; } else { cl = 4; } // Read any remaining bytes. if (cb < i + cl) { hr = readStream->Read(buffer + cb, i + cl - cb, &subsequentBytesRead); if (FAILED(hr)) { break; // Error, exit loop. } cb += subsequentBytesRead; } } // First determine the size required to store the Unicode string. int const sizeRequired = MultiByteToWideChar(CP_UTF8, 0, buffer, cb, nullptr, 0); if (sizeRequired == 0) { // Invalid UTF-8. hr = HRESULT_FROM_WIN32(GetLastError()); break; } unique_ptr<char16[]> wstr(new(std::nothrow) char16[sizeRequired + 1]); if (wstr.get() == nullptr) { hr = E_OUTOFMEMORY; break; } // Convert the string from UTF-8 to UTF-16LE. This can never fail, since // the previous call above succeeded. MultiByteToWideChar(CP_UTF8, 0, buffer, cb, wstr.get(), sizeRequired); wstr[sizeRequired] = L'\0'; // Terminate the string. ss << wstr.get(); // Write the string to the stream. } str = SUCCEEDED(hr) ? ss.str() : wstring(); return (SUCCEEDED(hr)) ? S_OK : hr; // Don't return S_FALSE. } // 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 { completionEvent.set(make_tuple<HRESULT, wstring>(move(hrError), wstring())); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } return hr; } // Retrieves the completion event for the HTTP operation. task_completion_event<tuple<HRESULT, wstring>> const& GetCompletionEvent() const { return completionEvent; } int GetStatusCode() const { return statusCode; } wstring GetReasonPhrase() const { return reasonPhrase; } private: ~HttpRequestStringCallback() { // Unregister the callback. if (cancellationToken != cancellation_token::none()) { cancellationToken.deregister_callback(registrationToken); } } // Signals that the download operation was canceled. cancellation_token cancellationToken; // Used to unregister the cancellation token callback. cancellation_token_registration registrationToken; // The IXMLHTTPRequest2 that processes the HTTP request. ComPtr<IXMLHTTPRequest2> request; // Task completion event that is set when the // download operation completes. task_completion_event<tuple<HRESULT, wstring>> completionEvent; int statusCode; wstring reasonPhrase; }; // Copy bytes from the sequential stream into the buffer provided until // we reach the end of one or the other. unsigned int Web::Details::HttpRequestBuffersCallback::ReadData( _Out_writes_(outputBufferSize) byte* outputBuffer, unsigned int outputBufferSize) { // Lock the data event while doing the read, to ensure that any bytes we don't read will // result in the correct event getting triggered. concurrency::critical_section::scoped_lock lock(dataEventLock); ULONG bytesRead; CheckHResult(dataStream.Get()->Read(outputBuffer, outputBufferSize, &bytesRead)); if (bytesRead < outputBufferSize) { // We need to reset the data event, which we can only do by creating a new one. dataEvent = task_completion_event<void>(); } return bytesRead; } // Create a task that completes when data is available, in an exception-safe way. task<void> Web::Details::HttpRequestBuffersCallback::CreateDataTask() { concurrency::critical_section::scoped_lock lock(dataEventLock); return create_task(dataEvent, cancellationToken); } HttpRequest::HttpRequest() : responseComplete(true), statusCode(200) { } // Start a download of the specified URI using the specified method. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). task<wstring> HttpRequest::DownloadAsync(PCWSTR httpMethod, PCWSTR uri, cancellation_token cancellationToken, PCWSTR contentType, IStream* postStream, uint64 postStreamSizeToSend) { // Create an IXMLHTTPRequest2 object. ComPtr<IXMLHTTPRequest2> xhr; CheckHResult(CoCreateInstance(CLSID_XmlHttpRequest, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&xhr))); // Create callback. auto stringCallback = Make<HttpRequestStringCallback>(xhr.Get(), cancellationToken); CheckHResult(stringCallback ? S_OK : E_OUTOFMEMORY); auto completionTask = create_task(stringCallback->GetCompletionEvent()); // Create a request. CheckHResult(xhr->Open(httpMethod, uri, stringCallback.Get(), nullptr, nullptr, nullptr, nullptr)); if (postStream != nullptr && contentType != nullptr) { CheckHResult(xhr->SetRequestHeader(L"Content-Type", contentType)); } // Send the request. CheckHResult(xhr->Send(postStream, postStreamSizeToSend)); // Return a task that completes when the HTTP operation completes. // We pass the callback to the continuation because the lifetime of the // callback must exceed the operation to ensure that cancellation // works correctly. return completionTask.then([this, stringCallback](tuple<HRESULT, wstring> resultTuple) { // If the GET operation failed, throw an Exception. CheckHResult(std::get<0>(resultTuple)); statusCode = stringCallback->GetStatusCode(); reasonPhrase = stringCallback->GetReasonPhrase(); return std::get<1>(resultTuple); }); } // Start an HTTP GET on the specified URI. The returned task completes once the entire response // has been received, and the task produces the HTTP response text. The status code and reason // can be read with GetStatusCode() and GetReasonPhrase(). task<wstring> HttpRequest::GetAsync(Uri^ uri, cancellation_token cancellationToken) { return DownloadAsync(L"GET", uri->AbsoluteUri->Data(), cancellationToken, nullptr, nullptr, 0); } void HttpRequest::CreateMemoryStream(IStream **stream) { auto randomAccessStream = ref new Windows::Storage::Streams::InMemoryRandomAccessStream(); CheckHResult(CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(stream))); } // Start an HTTP POST on the specified URI, using a string body. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). task<wstring> HttpRequest::PostAsync(Uri^ uri, const wstring& body, cancellation_token cancellationToken) { int length = 0; ComPtr<IStream> postStream; CreateMemoryStream(&postStream); if (body.length() > 0) { // Get the required buffer size. int size = WideCharToMultiByte(CP_UTF8, // UTF-8 0, // Conversion type body.c_str(), // Unicode string to convert static_cast<int>(body.length()), // Size nullptr, // Output buffer 0, // Output buffer size nullptr, nullptr); CheckHResult((size != 0) ? S_OK : HRESULT_FROM_WIN32(GetLastError())); std::unique_ptr<char[]> tempData(new char[size]); length = WideCharToMultiByte(CP_UTF8, // UTF-8 0, // Conversion type body.c_str(), // Unicode string to convert static_cast<int>(body.length()), // Size tempData.get(), // Output buffer size, // Output buffer size nullptr, nullptr); CheckHResult((length != 0) ? S_OK : HRESULT_FROM_WIN32(GetLastError())); CheckHResult(postStream->Write(tempData.get(), length, nullptr)); } return DownloadAsync(L"POST", uri->AbsoluteUri->Data(), cancellationToken, L"text/plain;charset=utf-8", postStream.Get(), length); } // Start an HTTP POST on the specified URI, using a stream body. The returned task produces the // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase(). task<wstring> HttpRequest::PostAsync(Uri^ uri, PCWSTR contentType, IStream* postStream, uint64 postStreamSizeToSend, cancellation_token cancellationToken) { return DownloadAsync(L"POST", uri->AbsoluteUri->Data(), cancellationToken, contentType, postStream, postStreamSizeToSend); } // Send a request but don't return the response. Instead, let the caller read it with ReadAsync(). task<void> HttpRequest::SendAsync(const wstring& httpMethod, Uri^ uri, cancellation_token cancellationToken) { // Create an IXMLHTTPRequest2 object. ComPtr<IXMLHTTPRequest2> xhr; CheckHResult(CoCreateInstance(CLSID_XmlHttpRequest, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&xhr))); // Create callback. buffersCallback = Make<Web::Details::HttpRequestBuffersCallback>(xhr.Get(), cancellationToken); CheckHResult(buffersCallback ? S_OK : E_OUTOFMEMORY); ComPtr<IXMLHTTPRequest2Callback> xhrCallback; CheckHResult(buffersCallback.As(&xhrCallback)); // Open and send the request. CheckHResult(xhr->Open(httpMethod.c_str(), uri->AbsoluteUri->Data(), xhrCallback.Get(), nullptr, nullptr, nullptr, nullptr)); responseComplete = false; CheckHResult(xhr->Send(nullptr, 0)); // Return a task that completes when the HTTP operation completes. // Since buffersCallback holds a reference on the callback, the lifetime of the callback will exceed // the operation and ensure that cancellation works correctly. return buffersCallback->CreateDataTask().then([this]() { CheckHResult(buffersCallback->GetError()); statusCode = buffersCallback->GetStatusCode(); reasonPhrase = buffersCallback->GetReasonPhrase(); }); } // Read a chunk of data from the HTTP response, up to a specified length or until we reach the end // of the response, and store the value in the provided buffer. This is useful for large content, // enabling the streaming of the result. task<void> HttpRequest::ReadAsync(Windows::Storage::Streams::IBuffer^ readBuffer, unsigned int offsetInBuffer, unsigned int requestedBytesToRead) { if (offsetInBuffer + requestedBytesToRead > readBuffer->Capacity) { throw ref new InvalidArgumentException(); } // Return a task that completes when a read completes. // We pass the callback to the continuation because the lifetime of the // callback must exceed the operation to ensure that cancellation // works correctly. return buffersCallback->CreateDataTask().then([this, readBuffer, offsetInBuffer, requestedBytesToRead]() { CheckHResult(buffersCallback->GetError()); // Get a pointer to the location to copy data into. ComPtr<IBufferByteAccess> bufferByteAccess; CheckHResult(reinterpret_cast<IUnknown*>(readBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess))); byte* outputBuffer; // Returned internal pointer, do not free this value. CheckHResult(bufferByteAccess->Buffer(&outputBuffer)); // Copy bytes from the sequential stream into the buffer provided until // we reach the end of one or the other. readBuffer->Length = buffersCallback->ReadData(outputBuffer + offsetInBuffer, requestedBytesToRead); if (buffersCallback->IsResponseReceived() && (readBuffer->Length < requestedBytesToRead)) { responseComplete = true; } }); }
使用 HttpRequest 类中在 Windows 应用商店 App
本节中 Windows 应用商店 app 演示如何使用 HttpRequest 选件类。 该应用程序提供定义了一个 URL 资源的框中,输入,并执行获取和发布操作的命令按钮并取消当前操作的按钮命令。
使用 HttpRequest 选件类
在 MainPage.xaml,如下所示请定义元素 StackPanel。
<StackPanel HorizontalAlignment="Left" Width="440" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="InputTextBox" TextWrapping="Wrap" Text="https://www.fourthcoffee.com/"/> <StackPanel Orientation="Horizontal"> <Button x:Name="GetButton" Content="Get" Background="Green" Click="GetButton_Click"/> <Button x:Name="PostButton" Content="Post" Background="Blue" Click="PostButton_Click"/> <Button x:Name="CancelButton" Content="Cancel" Background="Red" IsEnabled="False" Click="CancelButton_Click"/> <ProgressRing x:Name="ResponseProgressRing" /> </StackPanel> <TextBlock x:Name="ResponseTextBlock" TextWrapping="Wrap"/> </StackPanel>
在 MainPage.xaml.h,请将此 #include 指令:
#include "HttpRequest.h"
在 MainPage.xaml.h,请将这些 private 成员变量。MainPage 选件类:
// Produces HTTP requets. Web::HttpRequest m_httpRequest; // Enables us to cancel the active HTTP request. concurrency::cancellation_token_source m_cancelHttpRequestSource;
在 MainPage.xaml.h,声明 private 方法 ProcessHttpRequest:
// Displays the result of the provided HTTP request on the UI. void ProcessHttpRequest(concurrency::task<std::wstring> httpRequest);
在 MainPage.xaml.cpp,请将这些 using 语句:
using namespace concurrency; using namespace std; using namespace Web;
在 MainPage.xaml.cpp,请实现 GetButton_Click,PostButton_Click,并且,MainPage 的 CancelButton_Click 方法类别。
void MainPage::GetButton_Click(Object^ sender, RoutedEventArgs^ e) { // Create a new cancellation token source for the web request. m_cancelHttpRequestSource = cancellation_token_source(); // Set up the GET request parameters. auto uri = ref new Uri(InputTextBox->Text); auto token = m_cancelHttpRequestSource.get_token(); // Send the request and then update the UI. ProcessHttpRequest(m_httpRequest.GetAsync(uri, token)); } void MainPage::PostButton_Click(Object^ sender, RoutedEventArgs^ e) { // Create a new cancellation token source for the web request. m_cancelHttpRequestSource = cancellation_token_source(); // Set up the POST request parameters. auto uri = ref new Uri(InputTextBox->Text); wstring postData(L"This is sample POST data."); auto token = m_cancelHttpRequestSource.get_token(); // Send the request and then update the UI. ProcessHttpRequest(m_httpRequest.PostAsync(uri, postData, token)); } void MainPage::CancelButton_Click(Object^ sender, RoutedEventArgs^ e) { // Disable the Cancel button. // It will be re-enabled during the next web request. CancelButton->IsEnabled = false; // Initiate cancellation. m_cancelHttpRequestSource.cancel(); }
提示
如果您的应用程序不需要取消支持,通过 concurrency::cancellation_token::none 到 HttpRequest::GetAsync 和 HttpRequest::PostAsync 方法。
在 MainPage.xaml.cpp,请执行 MainPage::ProcessHttpRequest 方法。
// Displays the result of the provided HTTP request on the UI. void MainPage::ProcessHttpRequest(task<wstring> httpRequest) { // Enable only the Cancel button. GetButton->IsEnabled = false; PostButton->IsEnabled = false; CancelButton->IsEnabled = true; // Clear the previous response and start the progress ring. ResponseTextBlock->Text = ""; ResponseProgressRing->IsActive = true; // Create a continuation that shows the results on the UI. // The UI must be updated on the ASTA thread. // Therefore, schedule the continuation to run on the current context. httpRequest.then([this](task<wstring> previousTask) { try { // // Show the result on the UI. wstring response = previousTask.get(); if (m_httpRequest.GetStatusCode() == 200) { // The request succeeded. Show the response. ResponseTextBlock->Text = ref new String(response.c_str()); } else { // The request failed. Show the status code and reason. wstringstream ss; ss << L"The server returned " << m_httpRequest.GetStatusCode() << L" (" << m_httpRequest.GetReasonPhrase() << L')'; ResponseTextBlock->Text = ref new String(ss.str().c_str()); } } catch (const task_canceled&) { // Indicate that the operation was canceled. ResponseTextBlock->Text = "The operation was canceled"; } catch (Exception^ e) { // Indicate that the operation failed. ResponseTextBlock->Text = "The operation failed"; // TODO: Handle the error further. (void)e; } // Enable the Get and Post buttons. GetButton->IsEnabled = true; PostButton->IsEnabled = true; CancelButton->IsEnabled = false; // Stop the progress ring. ResponseProgressRing->IsActive = false; }, task_continuation_context::use_current()); }
在项目属性,在 链接器下,输入,指定 shcore.lib 和 msxml6.lib。
这是运行的应用程序:
后续步骤
请参见
参考
概念
用 C++ 为 Windows 应用商店应用程序创建异步操作
其他资源
Asynchronous programming in C++