RESTinio
sample/express_router/main.cpp
#include <iostream>
#include <restinio/all.hpp>
#include <json_dto/pub.hpp>
struct book_t
{
book_t() = default;
book_t(
std::string author,
std::string title )
: m_author{ std::move( author ) }
, m_title{ std::move( title ) }
{}
template < typename JSON_IO >
void
json_io( JSON_IO & io )
{
io
& json_dto::mandatory( "author", m_author )
& json_dto::mandatory( "title", m_title );
}
std::string m_author;
std::string m_title;
};
using book_collection_t = std::vector< book_t >;
namespace rr = restinio::router;
using router_t = rr::express_router_t<>;
class books_handler_t
{
public :
explicit books_handler_t( book_collection_t & books )
: m_books( books )
{}
books_handler_t( const books_handler_t & ) = delete;
books_handler_t( books_handler_t && ) = delete;
auto on_books_list(
const restinio::request_handle_t& req, rr::route_params_t ) const
{
auto resp = init_resp( req->create_response() );
resp.set_body(
"Book collection (book count: " +
std::to_string( m_books.size() ) + ")\n" );
for( std::size_t i = 0; i < m_books.size(); ++i )
{
resp.append_body( std::to_string( i + 1 ) + ". " );
const auto & b = m_books[ i ];
resp.append_body( b.m_title + "[" + b.m_author + "]\n" );
}
return resp.done();
}
auto on_book_get(
const restinio::request_handle_t& req, rr::route_params_t params )
{
const auto booknum = restinio::cast_to< std::uint32_t >( params[ "booknum" ] );
auto resp = init_resp( req->create_response() );
if( 0 != booknum && booknum <= m_books.size() )
{
const auto & b = m_books[ booknum - 1 ];
resp.set_body(
"Book #" + std::to_string( booknum ) + " is: " +
b.m_title + " [" + b.m_author + "]\n" );
}
else
{
resp.set_body(
"No book with #" + std::to_string( booknum ) + "\n" );
}
return resp.done();
}
auto on_author_get(
const restinio::request_handle_t& req, rr::route_params_t params )
{
auto resp = init_resp( req->create_response() );
try
{
auto author = restinio::utils::unescape_percent_encoding( params[ "author" ] );
resp.set_body( "Books of " + author + ":\n" );
for( std::size_t i = 0; i < m_books.size(); ++i )
{
const auto & b = m_books[ i ];
if( author == b.m_author )
{
resp.append_body( std::to_string( i + 1 ) + ". " );
resp.append_body( b.m_title + "[" + b.m_author + "]\n" );
}
}
}
catch( const std::exception & )
{
mark_as_bad_request( resp );
}
return resp.done();
}
auto on_new_book(
const restinio::request_handle_t& req, rr::route_params_t )
{
auto resp = init_resp( req->create_response() );
try
{
m_books.emplace_back(
json_dto::from_json< book_t >( req->body() ) );
}
catch( const std::exception & /*ex*/ )
{
mark_as_bad_request( resp );
}
return resp.done();
}
auto on_book_update(
const restinio::request_handle_t& req, rr::route_params_t params )
{
const auto booknum = restinio::cast_to< std::uint32_t >( params[ "booknum" ] );
auto resp = init_resp( req->create_response() );
try
{
auto b = json_dto::from_json< book_t >( req->body() );
if( 0 != booknum && booknum <= m_books.size() )
{
m_books[ booknum - 1 ] = b;
}
else
{
mark_as_bad_request( resp );
resp.set_body( "No book with #" + std::to_string( booknum ) + "\n" );
}
}
catch( const std::exception & /*ex*/ )
{
mark_as_bad_request( resp );
}
return resp.done();
}
auto on_book_delete(
const restinio::request_handle_t& req, rr::route_params_t params )
{
const auto booknum = restinio::cast_to< std::uint32_t >( params[ "booknum" ] );
auto resp = init_resp( req->create_response() );
if( 0 != booknum && booknum <= m_books.size() )
{
const auto & b = m_books[ booknum - 1 ];
resp.set_body(
"Delete book #" + std::to_string( booknum ) + ": " +
b.m_title + "[" + b.m_author + "]\n" );
m_books.erase( m_books.begin() + ( booknum - 1 ) );
}
else
{
resp.set_body(
"No book with #" + std::to_string( booknum ) + "\n" );
}
return resp.done();
}
private :
book_collection_t & m_books;
template < typename RESP >
static RESP
init_resp( RESP resp )
{
resp
.append_header( "Server", "RESTinio sample server /v.0.6" )
.append_header_date_field()
.append_header( "Content-Type", "text/plain; charset=utf-8" );
return resp;
}
template < typename RESP >
static void
mark_as_bad_request( RESP & resp )
{
resp.header().status_line( restinio::status_bad_request() );
}
};
auto server_handler( book_collection_t & book_collection )
{
auto router = std::make_unique< router_t >();
auto handler = std::make_shared< books_handler_t >( std::ref(book_collection) );
auto by = [&]( auto method ) {
using namespace std::placeholders;
return std::bind( method, handler, _1, _2 );
};
auto method_not_allowed = []( const auto & req, auto ) {
return req->create_response( restinio::status_method_not_allowed() )
.done();
};
// Handlers for '/' path.
router->http_get( "/", by( &books_handler_t::on_books_list ) );
router->http_post( "/", by( &books_handler_t::on_new_book ) );
// Disable all other methods for '/'.
router->add_handler(
restinio::http_method_get(), restinio::http_method_post() ),
// Handler for '/author/:author' path.
router->http_get( "/author/:author", by( &books_handler_t::on_author_get ) );
// Disable all other methods for '/author/:author'.
router->add_handler(
restinio::router::none_of_methods( restinio::http_method_get() ),
"/author/:author", method_not_allowed );
// Handlers for '/:booknum' path.
router->http_get(
R"(/:booknum(\d+))",
by( &books_handler_t::on_book_get ) );
router->http_put(
R"(/:booknum(\d+))",
by( &books_handler_t::on_book_update ) );
router->http_delete(
R"(/:booknum(\d+))",
by( &books_handler_t::on_book_delete ) );
// Disable all other methods for '/:booknum'.
router->add_handler(
restinio::http_method_get(),
restinio::http_method_post(),
restinio::http_method_delete() ),
R"(/:booknum(\d+))", method_not_allowed );
return router;
}
int main()
{
using namespace std::chrono;
try
{
using traits_t =
router_t >;
book_collection_t book_collection{
{ "Agatha Christie", "Murder on the Orient Express" },
{ "Agatha Christie", "Sleeping Murder" },
{ "B. Stroustrup", "The C++ Programming Language" }
};
restinio::on_this_thread< traits_t >()
.address( "localhost" )
.request_handler( server_handler( book_collection ) )
.read_next_http_message_timelimit( 10s )
.write_http_response_timelimit( 1s )
.handle_request_timeout( 1s ) );
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:421
restinio::utils::unescape_percent_encoding
RESTINIO_NODISCARD std::string unescape_percent_encoding(const string_view_t data)
Definition: percent_encoding.hpp:363
restinio::traits_t
Definition: traits.hpp:33
restinio::status_method_not_allowed
http_status_line_t status_method_not_allowed()
Definition: http_headers.hpp:2232
restinio::router
Definition: boost_regex_engine.hpp:17
restinio::run
void run(asio_ns::io_context &ioctx, run_on_this_thread_settings_t< Traits > &&settings)
Helper function for running http server until ctrl+c is hit.
Definition: http_server_run.hpp:216
restinio::status_code::method_not_allowed
constexpr http_status_code_t method_not_allowed
Definition: http_headers.hpp:2062
restinio::asio_timer_manager_t
Timer factory implementation using asio timers.
Definition: asio_timer_manager.hpp:31
restinio::single_threaded_ostream_logger_t
ostream_logger_t< null_mutex_t > single_threaded_ostream_logger_t
Definition: ostream_logger.hpp:102
restinio::status_bad_request
http_status_line_t status_bad_request()
Definition: http_headers.hpp:2217
restinio::request_handle_t
std::shared_ptr< request_t > request_handle_t
Request handler, that is the type for calling request handlers.
Definition: request_handler.hpp:182
restinio::router::none_of_methods
RESTINIO_NODISCARD impl::fixed_size_none_of_matcher_t< sizeof...(Args) > none_of_methods(Args &&...args)
A factory function that creates a method_matcher that allows a method if it isn't found in the list o...
Definition: method_matcher.hpp:449
restinio::response_connection_attr_t::connection_keepalive
@ connection_keepalive
This response says to keep connection.
nonstd::sv_lite::to_string
std::basic_string< CharT, Traits > to_string(basic_string_view< CharT, Traits > v)
Definition: string_view.hpp:1144
all.hpp