C++ REST SDK를 이용한 Rest API Server 구축

Brie·2023년 11월 6일
0

C++

목록 보기
6/9

개요

해당 문서에서는 C++에서 cpprestsdk를 통해 REST API Server를 만드는 방법에 대해서 설명한다.

사전 설정

본 문서에서는 cpprestsdk를 사용할 것이기 때문에, 해당 SDK를 사용할 수 있도록 사전 설정이 완료되어 있어야 한다.

cpprestsdk를 사용할 수 있도록 개발 환경을 설정하는 방법에 대해서는 해당 문서를 참조한다.

개발 환경

개발 환경은 다음과 같은 운영체제와 IDE를 사용하였다.

  • OS: Windows 11 Home 22H2
  • IDE: Visual Studio 2022

serverMain.cpp

코드를 작성하기 위해 Visual Studio에서 serverMain.cpp라는 소스 파일을 생성하였다.

#include <iostream>
#include <cpprest/http_listener.h>

using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;

http_listener를 사용하기 위해 cpprest의 헤더 파일을 include하고 namespace를 설정한다. 초기 설정은 cpprestsdk를 사용하여 Request를 보낼때와 비교해 크게 달라진 부분은 없다.

int main() {
	http_listener listener(U("http://localhost:9090"));        //Server URL, Port 지정.
	listener.support(methods::GET, [&listener](http_request req) {

		// 처리할 내용 입력

	});
}

http_listener는 정해진 Server URL, Port에 대한 HTTP 요청을 수신하고 응답을 생성하는 HTTP 서버의 역할을 수행한다.

listener.support()는 특정 HTTP method에 대한 요청을 받았을 때, 해당 요청에 대해 처리할 수 있도록 지원하는 역할을 한다.

	try {
	    listener.open().then([&listener]() {cout << "\nstart!!\n" << endl; }).wait(); // server open
	    cout << ("서버가 실행 중입니다...") << endl;
	    std::cin.get();
	    listener.close().wait();
	}
	catch (const std::exception& e) {
	    cout << "서버 실행 과정에서 오류가 발생했습니다." << endl;
	    wcerr << ("Error: ") << e.what() << endl;
	}
	
	return 0;

listener.open() 함수를 통해 서버를 열고 특정 문자가 입력될 때 까지 대기하는 상태를 만들어주었다. 또한 try ~ catch ~문을 통해 exception이 발생할 경우 오류 발생 메세지를 출력하고 exception의 종류를 출력해 확인할 수 있도록 했다.

listener.support(methods::GET, [&listener](http_request req) {                        //support() 함수를 통해 GET방식 지정
    // wcout << "uri: " << listener.uri().path() << endl;
    uri uri = req.relative_uri();
    auto path = uri.path();
    auto query = uri.query();
    wcout << "Path: " << path << endl;
    wcout << "Query: " << query << endl;

    auto name = uri::split_query(query)[U("name")];

    if (path == U("/")) {
        http_response response(status_codes::OK);
            response.headers().set_content_type(U("text/plain"));
            response.set_body(U("Hello, World!"));
            req.reply(response);
    }
    else if(path == U("/hello")){ // path에 대한 개별 처리
        req.reply(status_codes::OK, U("Hello, Other World!"));                        
    }
    else if (path == U("/hi")) { // path에 대한 개별 처리
        req.reply(status_codes::OK, U("Hello, Another World!"));                        
    }
    else if (path == U("/name")) { // path에 대한 개별 처리
        req.reply(status_codes::OK, U("Hello, ") + name + U("!"));
    }
    else { // 처리 구문이 존재하지 않는 요청에 대한 처리
        req.reply(status_codes::NotFound);
    }
    
});

listener.support() 내부에서 특정 path로 된 요청과 parameter에 대해 처리할 수 있도록 작성했다.

가장 먼저 서버에 대한 요청인 http_request req를 통해 uri를 가져오고, uri를 pathquery로 분리한다. path에 따라 클라이언트가 요청한 uri의 path에 따라 처리를 분리할 수 있고, query를 split_query() 함수를 통해 분리해 각 parameter에 대한 값을 얻어 처리할 수 있다.

Full Code

#include <iostream>
#include <cpprest/http_listener.h>

using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;

int main() {
    http_listener listener(U("http://localhost:9090"));        //Server URL, Port 지정.
    //listener.open().then([&listener]() {cout << (U("\n start!!\n")); }).wait();    //Server open
    listener.support(methods::GET, [&listener](http_request req) {                        //support() 함수를 통해 GET방식 지정
        uri uri = req.relative_uri();
        auto path = uri.path();
        auto query = uri.query();
        wcout << "Path: " << path << endl;
        wcout << "Query: " << query << endl;

        auto name = uri::split_query(query)[U("name")];

        if (path == U("/")) {
            http_response response(status_codes::OK);
            response.headers().set_content_type(U("text/plain"));
            response.set_body(U("Hello, World!"));
            req.reply(response);
        }
        else if(path == U("/hello")){ // path에 대한 개별 처리
            req.reply(status_codes::OK, U("Hello, Other World!"));                        
        }
        else if (path == U("/hi")) { // path에 대한 개별 처리
            req.reply(status_codes::OK, U("Hello, Another World!"));                        
        }
        else if (path == U("/name")) { // path에 대한 개별 처리
            req.reply(status_codes::OK, U("Hello, ") + name + U("!"));
        }
        //else if (path == U("/favicon.ico")) {} // favicon 요청에 대한 처리
        else { // 처리 구문이 존재하지 않는 요청에 대한 처리
            req.reply(status_codes::NotFound);
        }
        
    });

    try {
        listener.open().then([&listener]() {cout << "\nstart!!\n" << endl; }).wait(); // server open
        cout << ("서버가 실행 중입니다...") << endl;
        std::cin.get();
        listener.close().wait();
    }
    catch (const std::exception& e) {
        cout << "서버 실행 과정에서 오류가 발생했습니다." << endl;
        wcerr << ("Error: ") << e.what() << endl;
    }

    /*while (true);
    listener.close();*/
    return 0;
}

0개의 댓글