Sdílet prostřednictvím


Názorný postup: Připojení pomocí úlohy a požadavek HTTP XML (IXHR2)

Tento příklad ukazuje, jak používat rozhraní IXMLHTTPRequest2 a IXMLHTTPRequest2Callback spolu s úkoly odesílající požadavky HTTP GET a POST na webovou službu v aplikaci Windows Store.Kombinací IXHR2 spolu s úkoly můžete psát kód, který lze kombinovat s ostatními úkoly.Lze například použít úkol stahování jako součást řetězce úkolů.Úloha stahování může rovněž reagovat, když je práce zrušena.

Další informace o úkolech naleznete v části Úkol rovnoběžnosti (souběžnosti Runtime).Další informace o používání úkolů v aplikacích Windows Store najdete v tématech Asynchronous programming in C++ a Vytváření asynchronních operací v jazyce C++ pro aplikace pro web Windows Store.

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

Úplnější příklad, který používá třídu HttpReader popsanou v tomto dokumentu, lze nalézt v tématu Vývoj aplikace Bing Maps Trip Optimizer pro web Windows Store v jazycích JavaScript a C++.Další příklad, který používá IXHR2, ale nepoužívá úkoly, naleznete v tématu Quickstart: Connecting using XML HTTP Request (IXHR2).

Tip

IXMLHTTPRequest2 a IXMLHTTPRequest2Callback jsou rozhraní, která doporučujeme pro použití v aplikaci Windows Store.Tento příklad lze také upravit pro použití v aplikaci klasické pracovní plochy.

Definování tříd HttpRequest, HttpRequestBuffersCallback a HttpRequestStringCallback Classes

Při použití rozhraní IXMLHTTPRequest2 pro vytvoření webových požadavků přes protokol HTTP je implementováno rozhraní IXMLHTTPRequest2Callback pro příjem odpovědí serveru a pro reagování na další události.Tento příklad definuje třídu HttpRequest pro vytvoření webové žádosti a tříd HttpRequestBuffersCallback a HttpRequestStringCallback pro zpracovávání odpovědí procesu.Třídy HttpRequestBuffersCallback a HttpRequestStringCallback podporují třídu HttpRequest. Z kódu aplikace pracujete pouze se třídou HttpRequest.

Metody GetAsync a PostAsync třídy HttpRequest vám umožňují spustit operace HTTP GET a POST.Tyto metody používají třídu HttpRequestStringCallback pro čtení odpovědí serveru ve formě řetězce.Metody SendAsync a ReadAsync Vám umožňují přenášet v datovém proudu velký obsah po částech.Každá tato metoda vrací concurrency::task představující operaci.Metody GetAsync a PostAsync produkují hodnotu task<std::wstring>, kde část wstring představuje odpověď serveru.Metody SendAsync a ReadAsync produkují hodnoty task<void>. Tyto úkoly se dokončí po dokončení operací odesílání a čtení.

Protože rozhraní IXHR2 pracuje asynchronně, v tomto případě používá událost concurrency::task_completion_event pro vytvoření úkolu, který se dokončí poté, co objekt zpětného volání dokončí nebo zruší operaci stahování.Třída HttpRequest vytvoří z tohoto úkolu pokračování založené na úkolu pro nastavení konečného výsledku.Třída HttpRequest používá pokračování založené na úkolu, aby bylo zajištěno pokračování běhu úkolu, i když předchozí úkol vyprodukoval chybu nebo byl zrušen.Další informace o pokračování založeném na úkolech naleznete v tématu Úkol rovnoběžnosti (souběžnosti Runtime)

Pro podporu zrušení používají třídy HttpRequest, HttpRequestBuffersCallback a HttpRequestStringCallback tokeny zrušení.Třídy HttpRequestBuffersCallback a HttpRequestStringCallback používají metodu concurrency::cancellation_token::register_callback proto, aby mohla událost dokončení úkolu reagovat na zrušení.Toto zpětné volání zrušení přeruší stahování.Další informace o zrušení naleznete v tématu Zrušení v PPL.

Definování třídy HttpRequest

  1. Pro vytvoření projektu prázdné aplikace XAML použijte šablonu Prázdná aplikace (XAML) jazyka Visual C++.V tomto příkladu projekt pojmenujte UsingIXHR2.

  2. Přidejte do projektu soubor hlaviček pojmenovaný HttpRequest.h a zdrojový soubor s názvem HttpRequest.cpp.

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

    #include <ppltasks.h>
    #include <string>
    #include <sstream>
    #include <wrl.h>
    #include <msxml6.h>
    
  4. 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;
    };
    
    };
    
  5. Do 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 Windows Store

Tento oddíl ukazuje, jak použít třídu HttpRequest v aplikaci Windows Store.Aplikace poskytuje vstupní textové pole pro definici URL a tlačítka, která provádějí operace GET a POST, a tlačítko, které zruší aktuální operaci.

Použití třídy HttpRequest

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

    <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>
    
  2. Do souboru MainPage.xaml.h přidejte tyto směrnice #include:

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

    // 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 private metodu ProcessHttpRequest:

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

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

    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 pro zrušení, předejte concurrency::cancellation_token:: žádný metodám HttpRequest::GetAsync a HttpRequest::PostAsync.

  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. V dialogovém okně Vlastnosti projektu v kategorii Propojovací program zadejte do hodnoty vlastnosti Vstupshcore.lib a msxml6.lib.

Zde je spuštěná aplikace:

Spuštěné aplikace Windows Store

Další kroky

Souběžnost Runtime návody

Viz také

Referenční dokumentace

úkol třídy (souběžnosti Runtime)

Třída task_completion_event

Koncepty

Úkol rovnoběžnosti (souběžnosti Runtime)

Zrušení v PPL

Vytváření asynchronních operací v jazyce C++ pro aplikace pro web Windows Store

Další zdroje

Asynchronous programming in C++

Quickstart: Connecting using XML HTTP Request (IXHR2)

IXMLHTTPRequest2

IXMLHTTPRequest2Callback