C++ 编写的嵌入式应用程序,需要通过Web 服务器作为UI。用C++ 实现restfull web service 程序。我选择了restbed 架构(https://github.com/corvusoft/restbed)。
我使用的是windows WSL 形式的ubuntu OS,github 上安装讲的非常简单。
git clone --recursive https://github.com/corvusoft/restbed.git
mkdir restbed/build
cd restbed/build
cmake [-DBUILD_SSL=NO] ..
sudo make install
make test
实际安装过程中遇到了一些麻烦
sudo apt-get install libssl-dev
cd dependency/openssl
./config
make
在ubuntu 采取上述方式安装,也成功的。
编译应用程序的过程中发现缺少 libc++库,ssl库缺少的问题。
sudo apt-get update
sudo apt-get install libc++-dev
sudo apt-get install libssl-dev
restbed 程序make install 出来的include 和lib 放置在了restbed/distribution 目录下。要将include的内容 拷贝到 usr/include 中,而静态库librestbed.a 拷贝到应用程序的目录下。
我们编写了一个简单的应用程序 restbed_get.cpp 编译过程中要使用ssl,cryoto 和pthread 三个库。
sudo clang++ restbed_get.cpp -L./ -l restbed -o restbed_get -lssl -lcrypto –lpthread
restfull的机制是web 服务。所以在restbed 中,服务(service)是一个重要的对象。他可以发布一个资源(resource)。restfull API 就是给其它机器提供一个使用HTTP协议调用这些服务的机制。
#include <string>
#include <memory>
#include <cstdlib>
#include <fstream>
#include <restbed>
#include <streambuf>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
const string filename = request->get_path_parameter( "filename" );
ifstream stream( "./" + filename, ifstream::in );
if ( stream.is_open( ) )
{
const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
const multimap< string, string > headers
{
{ "Content-Type", "text/html" },
{ "Content-Length", ::to_string( body.length( ) ) }
};
session->close( OK, body, headers );
}
else
{
session->close( NOT_FOUND );
}
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/static/{filename: [a-z]*\\.html}" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
在程序的文件夹中丢一个index.html 文件
在浏览器上键入:http://localhost:1984/static/index.html,出现hello the world 的页面。
这个程序于实际应用更加接近一点。
定义了三个resource。可以调用html ,css 文档和web API 参数传递。
#include <string>
#include <memory>
#include <cstdlib>
#include <fstream>
#include <restbed>
#include <streambuf>
using namespace std;
using namespace restbed;
void get_index( const shared_ptr< Session > session )
{
// const auto request = session->get_request( );
ifstream stream( "./index.html", ifstream::in );
if ( stream.is_open( ) )
{
const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
const multimap< string, string > headers
{
{ "Content-Type", "text/html" },
{ "Content-Length", ::to_string( body.length( ) ) }
};
session->close( OK, body, headers );
}
else
{
session->close( NOT_FOUND );
}
}
void get_css( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
const string filename = request->get_path_parameter( "filename" );
ifstream stream( "./" + filename, ifstream::in );
if ( stream.is_open( ) )
{
const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
const multimap< string, string > headers
{
{ "Content-Type", "text/css" },
{ "Content-Length", ::to_string( body.length( ) ) }
};
session->close( OK, body, headers );
}
else
{
session->close( NOT_FOUND );
}
}
void get_value( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
const string id = request->get_query_parameter( "id" );
const string val = request->get_query_parameter( "val" );
const string body = "id="+id+",val="+val;
session->close( OK, body, { { "Content-Length", ::to_string( body.size( ) ) } } );
}
int main( const int, const char** )
{
auto resource1 = make_shared< Resource >( );
resource1->set_path( "/index" );
resource1->set_method_handler( "GET", get_index );
auto resource2 = make_shared< Resource >( );
resource2->set_path( "/css/{filename: [a-z]*\\.css}" );
resource2->set_method_handler( "GET", get_css);
auto resource3 = make_shared< Resource >( );
resource3->set_path( "/query/param");
resource3->set_method_handler( "GET", get_value);
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource1 );
service.publish( resource2 );
service.publish( resource3 );
service.start( settings );
return EXIT_SUCCESS;
}
在传递参数时遇到了些问题,花费了许多时间。
HTTP 请求向服务器端提供参数,具体的方式有两种:
1 路径参数方式
Path parameter example: http://localhost:8080/v1/api/customer/1,2,3/policy
2 查询参数方式
Query parameter example: http://localhost:8080/v1/api/customer?id=1,2,3
在restbed 中的实例中,多数使用的是path parameters。使用Query parameter 不知道如何使用,试了许多方法都不对。最后搞定了。只要记得一件事:?前面的是path parameter,而?后面的是query parameter。
例如在“http://localhost:8080/v1/api/customer?id=1,2,3”中,具体的做法是
resource->set_path( "/v1/api/customer ");
而在 get_method_handle 子程序中使用
const string parameters = request->get_query_parameter( "id" );
至此,restbed 基本好用了。