VC++ cpprestsdkを使ったHTTP通信

vc-cpprestsdkのアイキャッチ画像 Windows

MicrosoftがGitHubでオープンソースとして公開しているcpprestsdkを使い、VC++でHTTP通信する方法を解説します。REST APIを実行する場合に利用します。

環境構築

cpprestsdkのダウンロード

Visual StudioでパッケージマネージャーであるNuGetを使い、cpprestsdkをダウンロードします。

Visual Studioを起動し、新しいプロジェクトを作成します。
C++用の空のプロジェクトを作成します。

VisualStudio①

任意の場所に、任意の名前でプロジェクトを作成します。
ここでは、「C:\temp\application」にプロジェクトを作成します。

VisualStudio②

NuGetを使い、cpprestsdkをダウンロードします。

VisualStudio③
VisualStudio④
VisualStudio⑤

「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native」以下に、ヘッダとモジュール(dllファイルやlibファイルなど)がダウンロードできました。

cpprestsdkのファイル群①

「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native」以下のみを使います。作成したプロジェクトファイルなどは削除してOKです。

プロジェクトファイルの用意

こちらの通り、新たにプロジェクトファイル、プロパティファイルを作成します。その上で、以下を実施します。

ヘッダの配置

「C:\src\include\cpprestsdk」フォルダを作成し、「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native\include」の中身をコピーします。

cpprestsdkのファイル群②

モジュールの配置

「C:\src\build2019\cpprestsdk\debug」フォルダを作成し、「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native\x64\bin」と「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native\x64\lib」の中身の内、ファイル名に「d」が付いたファイルをコピーします。

cpprestsdkのファイル群③

コピー後、libファイルについては、ファイル名の「d」は削除しておきます。

cpprestsdkのファイル群④

次いで、「C:\src\build2019\cpprestsdk\release」フォルダを作成し、「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native\x64\bin」と「C:\temp\application\packages\cpprestsdk.<バージョン>\build\native\x64\lib」の中身の内、ファイル名に「d」が付いていないファイルをコピーします。

cpprestsdkのファイル群⑤

プロパティファイルの書き換え

cpprestsdkを使うため、プロパティファイルを以下のように書き換えます。

src/components/make.props

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets">
    <Import Project="..\make.props" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup>
    <_PropertySheetDisplayName>components</_PropertySheetDisplayName>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <AdditionalIncludeDirectories>$(HOGE_SRC_DIR)\include\cpprestsdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>  <!-- ★変更箇所 -->
    </ClCompile>
    <Link>
      <OutputFile>$(OutDir)$(ProjectName).exe</OutputFile>
      <AdditionalDependencies>cpprest141_2_10.lib;%(AdditionalDependencies)</AdditionalDependencies>  <!-- ★変更箇所 -->
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>

src/components/make_debug64.props

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets">
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup>
    <_PropertySheetDisplayName>components.debug64</_PropertySheetDisplayName>
    <OutDir>$(HOGE_BUILD_DIR)\components\bin\x64\Debug\</OutDir>
    <IntDir>$(HOGE_VER)\$(PlatformName)\$(Configuration)\</IntDir>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
    </ClCompile>
    <Link>
      <TargetMachine>MachineX64</TargetMachine>
      <AdditionalLibraryDirectories>$(HOGE_BUILD_DIR)\cpprestsdk\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>  <!-- ★変更箇所 -->
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>

src/components/make_release64.props

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets">
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup>
    <_PropertySheetDisplayName>components.releas64</_PropertySheetDisplayName>
    <OutDir>$(HOGE_BUILD_DIR)\components\bin\x64\Release\</OutDir>
    <IntDir>$(HOGE_VER)\$(PlatformName)\$(Configuration)\</IntDir>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>MaxSpeed</Optimization>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
      <WholeProgramOptimization>false</WholeProgramOptimization>
    </ClCompile>
    <Link>
      <TargetMachine>MachineX64</TargetMachine>
      <AdditionalLibraryDirectories>$(HOGE_BUILD_DIR)\cpprestsdk\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>  <!-- ★変更箇所 -->
      <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>

以上で、環境構築は完了です。

サンプルコード

cpprestsdkを使い、POST/GET/PUT/DELETEを行うサンプルコードを記述します。

「C:\src\components\component1\component1.vcxproj」に、「HttpClient.h」ファイルと「HttpClient.cpp」ファイルを追加し、HttpClientクラスを実装します。

VisualStudio⑥

以下のように実装します。

src/components/component1/HttpClient.h

#pragma once

#include <stdio.h>
#include "cpprest/http_client.h"
#include "cpprest/filestream.h"

using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;

// HTTP(S)クライアント
class HttpClient
{
public:

	// コンストラクタ
	HttpClient::HttpClient() { this->_m_nStatusCode = 0; }

	// POST
	int post(const std::string& inUrl, const json::value& inData, json::value& outData);

	// GET
	int get(const std::string& inUrl, json::value& outData);

	// PUT
	int put(const std::string& inUrl, const json::value& inData, json::value& outData);

	// DELETE
	int del(const std::string& inUrl, json::value& outData);

private:

	pplx::task<int> _post(const std::string& inUrl, const json::value& inData);

	pplx::task<int> _get(const std::string& inUrl);

	pplx::task<int> _put(const std::string& inUrl, const json::value& inData);

	pplx::task<int> _del(const std::string& inUrl);

private:

	json::value _m_cResultValue;
	int _m_nStatusCode;
};

src/components/component1/HttpClient.cpp

#include "HttpClient.h"

int HttpClient::post(const std::string& inUrl, const json::value& inData, json::value& outData)
{
	try {
		this->_post(inUrl, inData).wait();
		outData = this->_m_cResultValue;
	} catch (const std::exception& e) {
		::printf("<!> %s. %s \n", inUrl.c_str(), e.what());
		if (this->_m_nStatusCode == 0) return 1;
		else return this->_m_nStatusCode;
	}

	if (this->_m_nStatusCode == 200) return 0;
	else if (this->_m_nStatusCode == 0) return 1;
	else return this->_m_nStatusCode;
}

int HttpClient::get(const std::string& inUrl, json::value& outData)
{
	try {
		this->_get(inUrl).wait();
		outData = this->_m_cResultValue;
	} catch (const std::exception& e) {
		::printf("<!> %s. %s \n", inUrl.c_str(), e.what());
		if (this->_m_nStatusCode == 0) return 1;
		else return this->_m_nStatusCode;
	}

	if (this->_m_nStatusCode == 200) return 0;
	else if (this->_m_nStatusCode == 0) return 1;
	else return this->_m_nStatusCode;
}

int HttpClient::put(const std::string& inUrl, const json::value& inData, json::value& outData)
{
	try {
		this->_put(inUrl, inData).wait();
		outData = this->_m_cResultValue;
	} catch (const std::exception& e) {
		::printf("<!> %s. %s \n", inUrl.c_str(), e.what());
		if (this->_m_nStatusCode == 0) return 1;
		else return this->_m_nStatusCode;
	}

	if (this->_m_nStatusCode == 200) return 0;
	else if (this->_m_nStatusCode == 0) return 1;
	else return this->_m_nStatusCode;

	return 0;
}

int HttpClient::del(const std::string& inUrl, json::value& outData)
{
	try {
		this->_del(inUrl).wait();
		outData = this->_m_cResultValue;
	} catch (const std::exception& e) {
		::printf("<!> %s. %s \n", inUrl.c_str(), e.what());
		if (this->_m_nStatusCode == 0) return 1;
		else return this->_m_nStatusCode;
	}

	if (this->_m_nStatusCode == 200) return 0;
	else if (this->_m_nStatusCode == 0) return 1;
	else return this->_m_nStatusCode;

	return 0;
}

pplx::task<int> HttpClient::_post(const std::string& inUrl, const json::value& inData)
{
	const std::string sUrl = inUrl;
	const json::value cValue = inData;

	// 実行タスク生成
	return pplx::create_task([sUrl, cValue]
	{
		utility::string_t sUtf16 = utility::conversions::utf8_to_utf16(sUrl);
		http_client cClient(sUtf16);
		http_request cRequest(methods::POST);
		cRequest.set_body(cValue.serialize(), L"application/json");
		return cClient.request(cRequest);
	}).then([this, sUrl](http_response cResponse)
	{
		if (cResponse.status_code() == status_codes::OK) {
			::printf("[POST] %s success. \n", sUrl.c_str());
		} else {
			::printf("<!> [POST] %s failed. status=%d \n", sUrl.c_str(), cResponse.status_code());
		}
		this->_m_nStatusCode = cResponse.status_code();
		return cResponse.extract_json(true);
	}).then([this](json::value cJson)
	{
		this->_m_cResultValue = cJson;
		return 0;
	});
}

pplx::task<int> HttpClient::_get(const std::string& inUrl)
{
	const std::string sUrl = inUrl;

	// 実行タスク生成
	return pplx::create_task([sUrl]
	{
		utility::string_t sUtf16 = utility::conversions::utf8_to_utf16(sUrl);
		sUtf16 = uri::encode_uri(sUtf16, uri::components::component::query);
		http_client cClient(sUtf16);
		http_request cRequest(methods::GET);
		return cClient.request(cRequest);
	}).then([this, sUrl](http_response cResponse)
	{
		if (cResponse.status_code() == status_codes::OK) {
			::printf("[GET] %s success. \n", sUrl.c_str());
			http_headers cHeader = cResponse.headers();
		} else {
			::printf("<!> [GET] %s failed. status=%d \n", sUrl.c_str(), cResponse.status_code());
		}
		this->_m_nStatusCode = cResponse.status_code();
		return cResponse.extract_json(true);
	}).then([this](json::value cJson)
	{
		this->_m_cResultValue = cJson;
		return 0;
	});
}

pplx::task<int> HttpClient::_put(const std::string& inUrl, const json::value& inData)
{
	const std::string sUrl = inUrl;
	const json::value cValue = inData;

	// 実行タスク生成
	return pplx::create_task([sUrl, cValue]
	{
		utility::string_t sUtf16 = utility::conversions::utf8_to_utf16(sUrl);
		http_client cClient(sUtf16);
		http_request cRequest(methods::POST);
		cRequest.set_body(cValue.serialize(), L"application/json");
		return cClient.request(cRequest);
	}).then([this, sUrl](http_response cResponse)
	{
		if (cResponse.status_code() == status_codes::OK) {
			::printf("[PUT] %s success. \n", sUrl.c_str());
		} else {
			::printf("<!> [PUT] %s failed. status=%d \n", sUrl.c_str(), cResponse.status_code());
		}
		this->_m_nStatusCode = cResponse.status_code();
		return cResponse.extract_json(true);
	}).then([this](json::value cJson)
	{
		this->_m_cResultValue = cJson;
		return 0;
	});
}

pplx::task<int> HttpClient::_del(const std::string& inUrl)
{
	const std::string sUrl = inUrl;

	// 実行タスク生成
	return pplx::create_task([sUrl]
	{
		utility::string_t sUtf16 = utility::conversions::utf8_to_utf16(sUrl);
		sUtf16 = uri::encode_uri(sUtf16, uri::components::component::query);
		http_client cClient(sUtf16);
		http_request cRequest(methods::GET);
		return cClient.request(cRequest);
	}).then([this, sUrl](http_response cResponse)
	{
		if (cResponse.status_code() == status_codes::OK) {
			::printf("[DELETE] %s success. \n", sUrl.c_str());
			http_headers cHeader = cResponse.headers();
		} else {
			::printf("<!> [DELETE] %s failed. status=%d \n", sUrl.c_str(), cResponse.status_code());
		}
		this->_m_nStatusCode = cResponse.status_code();
		return cResponse.extract_json(true);
	}).then([this](json::value cJson)
	{
		this->_m_cResultValue = cJson;
		return 0;
	});
}

HttpClientクラスを呼び出すメインプログラムは、例えば以下のように実装します。

src/components/component1/main.cpp

#include "HttpClient.h"

int main(int argc, const char** argv)
{
	int ret;
	json::value cOutData;

	HttpClient cHttpClient;
	ret = cHttpClient.get("http://localhost", cOutData);  // localhostへGETする例
	if (ret == 0) {
		ret = cOutData[utility::conversions::utf8_to_utf16("result")].as_integer();

		::printf("result=%d \n", ret);
	}

	return 0;
}

ビルドすると、「src\build2019\components\bin\x64\Debug(Release)\component1.exe」が出力されます。同フォルダに「C:\src\build2019\cpprestsdk\debug(release)\cpprest141_2_10(d).dll」をコピーして下さい。

cpprestsdkのファイル群⑥

component1.exeを実行すると、HTTP通信が実行されます。

以上で、cpprestsdkを使ったHTTP通信をすることができるようになりました。

タイトルとURLをコピーしました