[C++] libcurl (3) 공개 API를 사용해보자 : HTTP POST
libcurl 라이브러리를 이용하여 HTTP POST 요청을 해본다.
POST 요청의 경우 일반적으로 계정을 나타내는 Authentication 인증 정보를 헤더에 요구할 경우가 많다.
그 헤더 종류 중 하나가 Basic Authentication이 있다.
"Authroization: Basic {username:password}"
{username:password}: 해당 계정 이름과 비밀번호를 Base64 변환을 통해 얻은 스트링을 담아 보내게 된다.
두 필드는 ':'로 구분되어야 하기 때문에 username에는 ':' 가 없어야한다.
HTTP POST 테스트
쉽게 HTTP POST를 테스트할 수 있는 사이트 중 다음을 이용해보도록 한다.
https://reqbin.com/req/o3vugw0p/post-json-string-with-basic-authentication
해당 사이트 왼편에서 POST JSON with Basic Token 항목을 클릭하면 관련 HTTP POST를 테스트해 볼 수 있다.
BasicAuth 토큰 선택에서 Username과 Password를 입력한 후
Generate Code 항목에서 Curl/Bash를 선택해준다.
다른 항목을 선택하면 다른 언어에서 해당 HTTP POST 예제를 볼 수 있다.
libcurl을 사용할 예정이기 때문에 요청에 실어야하는 대략적인 헤더와 바디 내용을 알 수 있다.
libcurl을 사용하여 POST 요청하기
1. Basic Authentication 헤더를 위한 Base64 변환
다음 포스트에서 사용한 코드를 사용할 예정이다.
https://narakit.tistory.com/236
2. json과 커스텀 데이터
이전 포스트에서 사용한 nlohmannjson의 json 헤더 파일을 사용하고 exchange_info 라는 커스텀 구조체를 사용하여 해당 데이터를 JSON 직렬화하여 POST 내용에 실어 보낸다.
BlockChainInfo.h
#pragma once
#include <nlohmannjson/json.hpp>
#include <string>
#include <cstring>
#include <map>
using namespace nlohmann;
struct exchange_info
{
double price_24h;
double volume_24h;
double last_trade_price;
std::string symbol;
static void from_json(const json& jdata, exchange_info& info)
{
info.price_24h = jdata.at("price_24h").get<double>();
info.volume_24h = jdata.at("volume_24h").get<double>();
info.last_trade_price = jdata.at("last_trade_price").get<double>();
info.symbol = jdata.at("symbol").get<std::string>();
}
json to_json()
{
json jobject;
jobject["price_24h"] = price_24h;
jobject["volumne_24h"] = volume_24h;
jobject["last_trade_price"] = last_trade_price;
jobject["symbol"] = symbol;
return jobject;
}
std::string to_string()
{
char buffer[256] = {};
snprintf(buffer, std::size(buffer),
"symbol[%s] : price (%.2lf), volume (%.2lf), last_trade_price (%.2lf)",
symbol.c_str(),
price_24h,
volume_24h,
last_trade_price);
return buffer;
}
};
3. libcurl을 이용한 코드 작성
curl_define.h
#pragma once
#ifndef _CURL_DEFINE_
#define _CURL_DEFINE_
#include <cstdio>
#include <curl/curl.h>
#include <curl/curlver.h>
#include <curl/easy.h>
#include <curl/urlapi.h>
#endif // !_CURL_DEFINE_
curl_http.h
#include <iostream>
#include "curl_define.h"
#include "encoding.h"
#include "BlockChainInfo.h"
static size_t write_buffer_callback(char* contents, size_t size, size_t nmemb, std::string* response)
{
size_t count = size * nmemb;
if (response != nullptr && count > 0)
{
response->append(contents, count);
}
return count;
}
static void http_post_request()
{
// 전역 curl 라이브러리 초기화
curl_global_init(CURL_GLOBAL_DEFAULT);
// curl 사용 시작
CURL* curl = curl_easy_init();
if (curl == nullptr)
{
std::cout << "init failed" << std::endl;
return;
}
// api URL 설정
static constexpr const char* url = "https://reqbin.com/echo/post/json";
curl_easy_setopt(curl, CURLOPT_URL, url);
// response 응답 콜백 등록
std::string response;
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
// Basic Auth
std::string authorizationString = "Authroization: Basic ";
authorizationString += Base64::Encode("hello:1234");
// http request 헤더 등록
curl_slist* slist = nullptr;
slist = curl_slist_append(slist, "Accept: application/json");
slist = curl_slist_append(slist, authorizationString.c_str());
slist = curl_slist_append(slist, "Content-Type : application/json");
slist = curl_slist_append(slist, "charset: utf-8");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
// POST 내용
exchange_info info;
info.price_24h = 10200;
info.volume_24h = 29822;
info.symbol = "BTC-USD";
info.last_trade_price = 10221;
auto jobject = info.to_json();
auto jsonStr = jobject.dump(0);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonStr);
// http GET 전송
CURLcode err_code = curl_easy_perform(curl);
if (err_code != CURLE_OK)
{
std::cout << "curl_easy_perform failed : " << curl_easy_strerror(err_code) << std::endl;
return;
}
// 응답 코드 및 내용 확인
std::size_t response_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
std::cout << "http response code : " << response_code << std::endl;
std::cout << "http response : " << response << std::endl;
// 리소스 해제
curl_slist_free_all(slist);
curl_easy_cleanup(curl);
curl_global_cleanup();
}
내용은 GET과 비슷하나 Basic Authentication을 위한 헤더 및 POST 내용을 직렬화한 JSON 스트링을 담는 차이가 있다.
POST에 담기는 JSON 스트링 내용
VS에서 JSON 시각화 도우미로 확인한 JSON 스트링값은 다음과 같다.
인증 헤더에 담기는 내용
다음 계정 정보로 Base64 변환한 인증 정보를 만들어내면 사이트에서는 다음 결과를 기대한다.
- username : hello
- password: 1234
올바른 변환 결과로 사용되어 요청된 것을 알 수 있다. encoding.h 내용은 직접 작성하였지만 어느 정도 유닛 테스트를 하였기 때문에 올바른 함수 수행을 어느 정도? 보장한다.
HTTP POST 결과
해당 사이트에서 테스트한 결과로 받을 수 있는 성공적인 응답과 같은 내용을 받을 수 있다.