Návod: Připojení pomocí úloh a žádostí XML HTTP

Tento příklad ukazuje, jak používat rozhraní IXMLHTTPRequest2 a IXMLHTTPRequest2Callback společně s úlohami k odesílání požadavků HTTP GET a POST do webové služby v aplikaci Univerzální platforma Windows (UPW). Propojením požadavku IXMLHTTPRequest2 s úkoly můžete psát kód, který lze kombinovat s ostatními úkoly. Jako součást řetězu úkolů můžete například použít úkol pro stažení. Úkol stahování může také reagovat při zrušení práce.

Tip

Sadu C++ REST SDK můžete také použít k provádění požadavků HTTP z aplikace pro UPW pomocí aplikace C++ nebo z desktopové aplikace C++. Další informace najdete v sadě C++ REST SDK (codename "Casablanca").

Další informace o úkolech naleznete v tématu Paralelismus úkolu. Další informace o tom, jak používat úlohy v aplikaci pro UPW, naleznete v tématu Asynchronní programování v jazyce C++ a Vytváření asynchronních operací v jazyce C++ pro aplikace pro UPW.

Tento dokument nejprve ukazuje, jak vytvořit HttpRequest a jeho podpůrné třídy. Pak ukazuje, jak používat tuto třídu z aplikace pro UPW, která používá C++ a XAML.

Příklad, který používáIXMLHTTPRequest2, ale nepoužívá úlohy, najdete v tématu Rychlý start: Připojení použití požadavku HTTP XML (IXMLHTTPRequest2).

Tip

IXMLHTTPRequest2 a IXMLHTTPRequest2Callback jsou rozhraní, která doporučujeme použít v aplikaci pro UPW. Tento příklad můžete také přizpůsobit pro použití v desktopové aplikaci.

Požadavky

Podpora UPW je v sadě Visual Studio 2017 a novější volitelná. Pokud ho chcete nainstalovat, otevřete Instalační program pro Visual Studio z windows nabídka Start a zvolte verzi sady Visual Studio, kterou používáte. Klikněte na tlačítko Upravit a ujistěte se, že je zaškrtnutá dlaždice Vývoj pro UPW. V části Volitelné komponenty se ujistěte, že jsou zaškrtnuté nástroje upW jazyka C++. Použijte v141 pro Visual Studio 2017 nebo v142 pro Visual Studio 2019.

Definice tříd HttpRequest, HttpRequestBuffersCallback a HttpRequestStringCallback

Když rozhraní použijete IXMLHTTPRequest2 k vytváření webových požadavků přes PROTOKOL HTTP, implementujete IXMLHTTPRequest2Callback rozhraní pro příjem odezvy serveru a reakci na jiné události. Tento příklad definuje HttpRequest třídu pro vytváření webových požadavků a HttpRequestBuffersCallback třídy pro HttpRequestStringCallback zpracování odpovědí. Třídy HttpRequestBuffersCallback podporují HttpRequest třídu. Pracujete pouze s HttpRequest třídou HttpRequestStringCallback z kódu aplikace.

Metody GetAsyncPostAsyncHttpRequest třídy umožňují spustit operace HTTP GET a POST v uvedeném pořadí. Tyto metody používají HttpRequestStringCallback třídu ke čtení odpovědi serveru jako řetězce. ReadAsync Metody SendAsync umožňují streamovat velký obsah v blocích. Tyto metody vrátí každou souběžnost::task představující operaci. Metody GetAsync vytvářejí task<std::wstring> hodnotu, kde wstring část PostAsync představuje odpověď serveru. ReadAsync Tyto SendAsync metody vytvářejí task<void> hodnoty; tyto úlohy jsou dokončeny po dokončení operací odesílání a čtení.

Vzhledem k tomu, že IXMLHTTPRequest2 rozhraní fungují asynchronně, tento příklad používá souběžnost::task_completion_event k vytvoření úlohy, která se dokončí po dokončení objektu zpětného volání nebo zruší operaci stahování. Třída HttpRequest vytvoří pokračování na základě úkolu z tohoto úkolu k nastavení konečného výsledku. Třída HttpRequest používá pokračování založené na úlohách, aby se zajistilo, že se úloha pokračování spustí i v případě, že předchozí úloha způsobí chybu nebo je zrušena. Další informace o pokračováních založených na úlohách naleznete v tématu Paralelismus úkolu.

Chcete-li podporovat zrušení, , HttpRequestHttpRequestBuffersCallbacka HttpRequestStringCallback třídy používat tokeny zrušení. HttpRequestStringCallback Třídy HttpRequestBuffersCallback používají metodu concurrency::cancellation_token::register_callback, aby událost dokončení úkolu mohla reagovat na zrušení. Zrušení zpětného volání přeruší stahování. Další informace o zrušení najdete v tématu Zrušení.

Definice třídy HttpRequest

  1. V hlavní nabídce zvolte Soubor>nový>projekt.

  2. Pomocí šablony prázdná aplikace C++ (Universal Windows) vytvořte prázdný projekt aplikace XAML. Tento příklad pojmenuje projekt UsingIXMLHTTPRequest2.

  3. Přidejte do projektu hlavičkový soubor s názvem HttpRequest.h a zdrojový soubor s názvem HttpRequest.cpp.

  4. Do souboru pch.h přidejte tento kód:

    #include <ppltasks.h>
    #include <string>
    #include <sstream>
    #include <wrl.h>
    #include <msxml6.h>
    
  5. Do httpRequest.h přidejte tento kód:

    #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. Do souboru HttpRequest.cpp přidejte tento kód:

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

Použití třídy HttpRequest v aplikaci pro UPW

Tato část ukazuje, jak používat HttpRequest třídu v aplikaci pro UPW. Aplikace poskytuje vstupní pole, které definuje prostředek adresy URL a příkazy tlačítka, které provádějí operace GET a POST, a příkaz tlačítka, který zruší aktuální operaci.

Použití třídy HttpRequest

  1. V MainPage.xaml, definujte StackPanel element následujícím způsobem.

    <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. Do souboru MainPage.xaml.h přidejte tuto #include direktivu:

    #include "HttpRequest.h"
    
  3. V MainPage.xaml.h přidejte tyto private členské proměnné do MainPage třídy:

    // Produces HTTP requets.
    Web::HttpRequest m_httpRequest;
    // Enables us to cancel the active HTTP request.
    concurrency::cancellation_token_source m_cancelHttpRequestSource;
    
  4. V Souboru MainPage.xaml.h deklarujte metodu privateProcessHttpRequest:

    // Displays the result of the provided HTTP request on the UI.
    void ProcessHttpRequest(concurrency::task<std::wstring> httpRequest);
    
  5. V souboru MainPage.xaml.cpp přidejte tyto using příkazy:

    using namespace concurrency;
    using namespace std;
    using namespace Web;
    
  6. V MainPage.xaml.cpp, implementujte GetButton_Click, PostButton_Clicka CancelButton_Click metody MainPage třídy.

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

    Tip

    Pokud vaše aplikace nevyžaduje podporu zrušení, předejte souběžnost::cancellation_token::none metodám a HttpRequest::PostAsync metodámHttpRequest::GetAsync.

  7. V Souboru MainPage.xaml.cpp implementujte metodu 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());
    }
    
  8. Ve vlastnostech projektu v části Linker, Input, zadejte shcore.lib a msxml6.lib.

Tady je spuštěná aplikace:

The running Windows Runtime app.

Další kroky

Návody pro Concurrency Runtime

Viz také

Paralelismus úkolu
Zrušení v knihovně PPL
Asynchronní programování v jazyce C++
Vytváření asynchronních operací v jazyce C++ pro aplikace pro UPW
Rychlý start: Připojení použití požadavku HTTP XML (IXMLHTTPRequest2)task – třída (Concurrency Runtime)
task_completion_event – třída