Exemplarische Vorgehensweise: Verbinden von Verwendungsaufgaben und XML-HTTP-AnforderungenWalkthrough: Connecting Using Tasks and XML HTTP Requests

Dieses Beispiel zeigt, wie die IXMLHTTPRequest2 -und IXMLHTTPRequest2Callback -Schnittstellen in Verbindung mit Aufgaben verwendet werden, um HTTP Get-und Post-Anforderungen an einen Webdienst in einer universelle Windows-Plattform-app (UWP) zu senden.This example shows how to use the IXMLHTTPRequest2 and IXMLHTTPRequest2Callback interfaces together with tasks to send HTTP GET and POST requests to a web service in a Universal Windows Platform (UWP) app. Beim Kombinieren von IXMLHTTPRequest2 mit Aufgaben können Sie Code schreiben, der mit anderen Aufgaben zusammen erstellt wird.By combining IXMLHTTPRequest2 together with tasks, you can write code that composes with other tasks. Beispielsweise können Sie die Downloadaufgabe als Teil einer Kette von Aufgaben verwenden.For example, you can use the download task as part of a chain of tasks. Wenn Arbeit abgebrochen wird, kann die Downloadaufgabe auch weiterhin antworten.The download task can also respond when work is canceled.

Tipp

Sie können auch das C++-Rest-SDK verwenden, um HTTP-Anforderungen von einer UWP-App mithilfe der C++-APP oder einer Desktop C++-App auszuführen.You can also use the C++ REST SDK to perform HTTP requests from a UWP app using C++ app or from a desktop C++ app. Weitere Informationen finden Sie unter C++ Rest SDK (Codename "Casablanca").For more info, see C++ REST SDK (Codename "Casablanca").

Weitere Informationen zu Aufgaben finden Sie Unteraufgaben Parallelität.For more information about tasks, see Task Parallelism. Weitere Informationen zum Verwenden von Aufgaben in einer UWP-App finden Sie unter asynchrone Programmierung in C++ und Erstellen von asynchronen Vorgängen in C++ für UWP-apps.For more information about how to use tasks in a UWP app, see Asynchronous programming in C++ and Creating Asynchronous Operations in C++ for UWP Apps.

Dieses Dokument erläutert zunächst, wie die HttpRequest-Klasse und unterstützende Klassen erstellt werden.This document first shows how to create HttpRequest and its supporting classes. Anschließend wird gezeigt, wie diese Klasse aus einer UWP-App verwendet wird, die C++ und XAML verwendet.It then shows how to use this class from a UWP app that uses C++ and XAML.

Ein Beispiel, das verwendet IXMLHTTPRequest2 , aber keine Aufgaben verwendet, finden Sie unter Schnellstart: Herstellen einer Verbindung mithilfe einer XML-HTTP-Anforderung (IXMLHTTPRequest2).For an example that uses IXMLHTTPRequest2 but does not use tasks, see Quickstart: Connecting using XML HTTP Request (IXMLHTTPRequest2).

Tipp

IXMLHTTPRequest2 und IXMLHTTPRequest2Callback sind die Schnittstellen, die wir für die Verwendung in einer UWP-App empfehlen.IXMLHTTPRequest2 and IXMLHTTPRequest2Callback are the interfaces that we recommend for use in a UWP app. Darüber hinaus können Sie dieses Beispiel auch für die Verwendung in einer Desktop-App anpassen.You can also adapt this example for use in a desktop app.

VoraussetzungenPrerequisites

UWP-Unterstützung ist in Visual Studio 2017 und höher optional.UWP support is optional in Visual Studio 2017 and later. Öffnen Sie die Visual Studio-Installer über das Windows-Startmenü, und wählen Sie die Version von Visual Studio aus, die Sie verwenden, um Sie zu installieren.To install it, open the Visual Studio Installer from the Windows Start menu and choose the version of Visual Studio you are using. Klicken Sie auf die Schaltfläche ändern , und vergewissern Sie sich, dass die Kachel UWP Development aktiviert ist.Click the Modify button and make sure the UWP Development tile is checked. Stellen Sie sicher, dass unter optionale Komponenten die Option C++ UWP Tools aktiviert ist.Under Optional Components make sure that C++ UWP Tools is checked. Verwenden Sie v141 für Visual Studio 2017 oder v142 für Visual Studio 2019.Use v141 for Visual Studio 2017 or v142 for Visual Studio 2019.

Definieren der HttpRequest-, HttpRequestBuffersCallback- und HttpRequestStringCallback-KlassenDefining the HttpRequest, HttpRequestBuffersCallback, and HttpRequestStringCallback Classes

Wenn Sie die IXMLHTTPRequest2-Schnittstelle verwenden, um Webanforderungen über HTTP zu erstellen, implementieren Sie die IXMLHTTPRequest2Callback-Schnittstelle, um die Serverantwort zu empfangen und auf andere Ereignisse zu reagieren.When you use the IXMLHTTPRequest2 interface to create web requests over HTTP, you implement the IXMLHTTPRequest2Callback interface to receive the server response and react to other events. In diesem Beispiel werden die HttpRequest-Klasse zum Erstellen von Webanforderungen und die Klassen HttpRequestBuffersCallback und HttpRequestStringCallback zum Verarbeiten der Antworten definiert.This example defines the HttpRequest class to create web requests, and the HttpRequestBuffersCallback and HttpRequestStringCallback classes to process responses. Die HttpRequestBuffersCallback-Klasse und die HttpRequestStringCallback-Klasse unterstützen die HttpRequest-Klasse; Sie arbeiten im Anwendungscode nur mit der HttpRequest-Klasse.The HttpRequestBuffersCallback and HttpRequestStringCallback classes support the HttpRequest class; you work only with the HttpRequest class from application code.

Die GetAsync-Methode und die PostAsync-Methode der HttpRequest-Klasse ermöglichen Ihnen das Durchführen von HTTP-Anforderungen (GET, POST).The GetAsync, PostAsync methods of the HttpRequest class enable you to start HTTP GET and POST operations, respectively. Diese Methoden verwenden die HttpRequestStringCallback-Klasse, um die Serverantwort als Zeichenfolge zu lesen.These methods use the HttpRequestStringCallback class to read the server response as a string. Die SendAsync-Methode und die ReadAsync-Methode ermöglichen das Übertragen von umfangreichen Inhalten in Blöcken.The SendAsync and ReadAsync methods enable you to stream large content in chunks. Diese Methoden geben jeweils " parallelcurrency:: Task " zurück, um den Vorgang darzustellen.These methods each return concurrency::task to represent the operation. Die GetAsync-Methode und die PostAsync-Methode generieren task<std::wstring>-Wert, wobei der wstring-Teil die Antwort des Servers darstellt.The GetAsync and PostAsync methods produce task<std::wstring> value, where the wstring part represents the server’s response. Die SendAsync-Methode und die ReadAsync-Methode generieren task<void>-Werte. Diese Aufgaben werden abgeschlossen, wenn die „Send“- und „Read“-Vorgänge abgeschlossen werden.The SendAsync and ReadAsync methods produce task<void> values; these tasks complete when the send and read operations complete.

Da die IXMLHTTPRequest2 Schnittstellen asynchron agieren, wird in diesem Beispiel " parallelcurrency:: task_completion_event " verwendet, um eine Aufgabe zu erstellen, die abgeschlossen wird, nachdem das Rückruf Objekt den Downloadvorgang abgeschlossen oder abgebrochen hat.Because the IXMLHTTPRequest2 interfaces act asynchronously, this example uses concurrency::task_completion_event to create a task that completes after the callback object completes or cancels the download operation. Die HttpRequest-Klasse erstellt eine aufgabenbasierte Fortsetzung aus dieser Aufgabe, um das Endergebnis festzulegen.The HttpRequest class creates a task-based continuation from this task to set the final result. Die HttpRequest-Klasse verwendet eine aufgabenbasierte Fortsetzung, um sicherzustellen, dass die Fortsetzungsaufgabe ausgeführt wird, auch wenn die vorherige Aufgabe einen Fehler erzeugt oder abgebrochen wird.The HttpRequest class uses a task-based continuation to ensure that the continuation task runs even if the previous task produces an error or is canceled. Weitere Informationen zu Task basierten Fortsetzungen finden Sie unter Aufgaben Parallelität .For more information about task-based continuations, see Task Parallelism

Um ein Abbrechen zu unterstützen, verwenden die Klassen HttpRequest, HttpRequestBuffersCallback und HttpRequestStringCallback Abbruchtoken.To support cancellation, the HttpRequest, HttpRequestBuffersCallback, and HttpRequestStringCallback classes use cancellation tokens. Die HttpRequestBuffersCallback -Klasse und die- HttpRequestStringCallback Klasse verwenden die parallelcurrency:: cancellation_token:: Register_callback -Methode, um das Aufgaben Abschluss Ereignis für die Reaktion auf den Abbruch zu aktivieren.The HttpRequestBuffersCallback and HttpRequestStringCallback classes use the concurrency::cancellation_token::register_callback method to enable the task completion event to respond to cancellation. Dieser Abbruchsrückruf bricht den Download ab.This cancellation callback aborts the download. Weitere Informationen zum Abbruch finden Sie unter Abbruch.For more info about cancellation, see Cancellation.

So definieren Sie die HttpRequest-KlasseTo Define the HttpRequest Class

  1. Klicken Sie im Hauptmenü auf Datei > neu > Projekt.From the main menu, choose File > New > Project.

  2. Verwenden Sie die Vorlage C++ blank app (universelle Windows -Vorlage), um ein leeres XAML-App-Projekt zu erstellen.Use the C++ Blank App (Universal Windows) template to create a blank XAML app project. In diesem Beispiel wird das Projekt UsingIXMLHTTPRequest2genannt.This example names the project UsingIXMLHTTPRequest2.

  3. Fügen Sie dem Projekt eine Headerdatei mit dem Namen "HttpRequest.h" und eine Quelldatei mit dem Namen "HttpRequest.cpp" hinzu.Add to the project a header file that is named HttpRequest.h and a source file that is named HttpRequest.cpp.

  4. Fügen Sie "pch.h" diesen Code hinzu:In pch.h, add this code:

    #include <ppltasks.h>
    #include <string>
    #include <sstream>
    #include <wrl.h>
    #include <msxml6.h>
    
  5. Fügen Sie "HttpRequest.h" diesen Code hinzu:In HttpRequest.h, add this code:

    #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;
    };
    
    };
    
  6. Fügen Sie "HttpRequest.cpp" diesen Code hinzu:In HttpRequest.cpp, add this code:

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

Verwenden der HttpRequest-Klasse in einer UWP-AppUsing the HttpRequest Class in a UWP App

In diesem Abschnitt wird veranschaulicht, wie die- HttpRequest Klasse in einer UWP-App verwendet wird.This section demonstrates how to use the HttpRequest class in a UWP app. Die App enthält ein Eingabefeld, das eine URL-Ressource definiert, und Schaltflächen zum Durchführen von GET- und POST-Anforderungen und zum Abbrechen des aktuellen Vorgangs.The app provides an input box that defines a URL resource, and button commands that perform GET and POST operations, and a button command that cancels the current operation.

So verwenden Sie die HttpRequest-KlasseTo Use the HttpRequest Class

  1. Definieren Sie in "MainPage. XAML" das StackPanel -Element wie folgt.In MainPage.xaml, define the StackPanel element as follows.

    <StackPanel HorizontalAlignment="Left" Width="440"
                Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBox x:Name="InputTextBox" TextWrapping="Wrap" 
                 Text="http://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>
    
  2. In "MainPage.xaml.h" fügen Sie diese #include-Anweisung hinzu:In MainPage.xaml.h, add this #include directive:

    #include "HttpRequest.h"
    
  3. Fügen Sie in "MainPage. XAML. h" diese Element private Variablen der- MainPage Klasse hinzu:In MainPage.xaml.h, add these private member variables to the MainPage class:

    // Produces HTTP requets.
    Web::HttpRequest m_httpRequest;
    // Enables us to cancel the active HTTP request.
    concurrency::cancellation_token_source m_cancelHttpRequestSource;
    
  4. Deklarieren Sie in "MainPage. XAML. h" die- private Methode ProcessHttpRequest :In MainPage.xaml.h, declare the private method ProcessHttpRequest:

    // Displays the result of the provided HTTP request on the UI.
    void ProcessHttpRequest(concurrency::task<std::wstring> httpRequest);
    
  5. Fügen Sie in "MainPage. XAML. cpp" die folgenden using Anweisungen hinzu:In MainPage.xaml.cpp, add these using statements:

    using namespace concurrency;
    using namespace std;
    using namespace Web;
    
  6. Implementieren Sie in "MainPage.xaml.cpp" die Methoden GetButton_Click, PostButton_Click und CancelButton_Click aus der MainPage-Klasse.In MainPage.xaml.cpp, implement the GetButton_Click, PostButton_Click, and CancelButton_Click methods of the MainPage class.

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

    Tipp

    Wenn Ihre APP keine Unterstützung für den Abbruch benötigt, übergeben Sie " parallelcurrency:: cancellation_token:: None " an die HttpRequest::GetAsync -Methode und die- HttpRequest::PostAsync Methode.If your app does not require support for cancellation, pass concurrency::cancellation_token::none to the HttpRequest::GetAsync and HttpRequest::PostAsync methods.

  7. Implementieren Sie die MainPage::ProcessHttpRequest-Methode in "MainPage.xaml.cpp".In MainPage.xaml.cpp, implement the MainPage::ProcessHttpRequest method.

    // 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());
    }
    
  8. Geben Sie in den Projekteigenschaften unter Linker, Eingabe, shcore.lib und an msxml6.lib .In the project properties, under Linker, Input, specify shcore.lib and msxml6.lib.

Hier ist die ausgeführte App:Here is the running app:

Die Windows-Runtime APP wird ausgeführt.The running Windows Runtime app

Nächste SchritteNext Steps

Exemplarische Vorgehensweisen Concurrency RuntimeConcurrency Runtime Walkthroughs

Siehe auchSee also

Aufgaben ParallelitätTask Parallelism
Abbruch in der PPLCancellation in the PPL
Asynchrone Programmierung in C++Asynchronous programming in C++
Erstellen von asynchronen Vorgängen in C++ für UWP-appsCreating Asynchronous Operations in C++ for UWP Apps
Schnellstart: Herstellen einer Verbindung mithilfe einer XML-HTTP-Anforderung (IXMLHTTPRequest2) Task-Klasse (Concurrency Runtime)Quickstart: Connecting using XML HTTP Request (IXMLHTTPRequest2) task Class (Concurrency Runtime)
task_completion_event-Klassetask_completion_event Class