//My client class
class VoucherClient : public oatpp::web::client::ApiClient
{
#include OATPP_CODEGEN_BEGIN(ApiClient)
API_CLIENT_INIT(VoucherClient)
API_CALL_ASYNC("POST", "/", getRoot)
API_CALL("POST", "/connect", connect, BODY_STRING(String, ip))
#include OATPP_CODEGEN_END(ApiClient)
};
//My API call function :
void funcCall(shared_ptr<VoucherClient> client)
{
cout << "[Connect] Calling the server..." << endl;
auto data = client->connect("127.0.0.1")->readBodyToString(); //Crash dans le thread
OATPP_LOGD("CLIENT", "[connect] data='%s'", data->c_str());
}
//My run function :
void run()
{
cout << "Creating objectmapper" << endl;
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
cout << "Creating requestexecutor" << endl;
auto requestExecutor = createOatppExecutor();
cout << "Creating client" << endl;
auto client = VoucherClient::createShared(requestExecutor, objectMapper);
std::thread th(client);
}
//My main function :
int main()
{
oatpp::base::Environment::init();
printHelp();
run();
oatpp::base::Environment::destroy();
cout << "Press any key to close the program" << endl;
_getch();
return 0;
}
Hi,
I am evaluating simple/efficient and dependency-lite framework to create REST C++ backend for WEB app frontend which will be in typescript and PostgreSql as database.
So short question is: am I in the right place? :)
Also, I would like to have some Swagger that can "use" (read: serialize/load) to/from JSON and C++ model classes. This is not necessary, but nice bonus.
I tried PHP, but I really don't feel comfortable with it. Python is bit slow for my taste, so maybe its time to get back home to the one I love - C++.
Thanks in advance
Has there been any consideration for adding support for the http/2 spec and eventually http/3?
@JeremyGuinn , http2/3 isn't a priority for oatpp at the moment.
The main reason is that http2/3 are mainly designed for better multimedia content delivery - serving APIs is arguably more effective with HTTP 1.1 (depends on the application)
However, I think it should be possible to integrate oatpp with ex.: h2o server
The browser and server are upgraded to websocket protocol and the connection is established successfully. Does the server support sending data actively?
What solution should be set if you want the server to actively send data information after the connection is established?
Hey @shijiantouzouyiqie , sure - when a client is connected to the server, you'r getting the WebSocket
object representing the client -
https://github.com/oatpp/example-websocket/blob/master/server/src/websocket/WSListener.cpp#L43.
Then you can write data to that socket, using sendOneFrameText
or sendOneFrameBinary
methods.
Hi,
I am evaluating simple/efficient and dependency-lite framework to create REST C++ backend for WEB app frontend which will be in typescript and PostgreSql as database.
So short question is: am I in the right place? :)
Also, I would like to have some Swagger that can "use" (read: serialize/load) to/from JSON and C++ model classes. This is not necessary, but nice bonus.
Hello @nikoladsp , yes, you are in the right place :)
Please check-out this example project - https://github.com/oatpp/example-postgresql
It's basically a web-service which talks to postgres, and has swagger-ui
I got a basic question. Is an Oat++ server a standalone executable or does it require a web server to run in production, with the custom logic running as CGI or .dll or something like that? I have a custom application in mind and I need it to be easy to run and install.
Hello @peterritter , yes, Oat++ server is a standalone executable.
void WSInstanceListener::onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr<const ParameterMap>& params) {
socket.sendOneFrameText("Hello");
SOCKETS ++;
OATPP_LOGD(TAG, "New Incoming Connection. Connection count=%d", SOCKETS.load());
/* In this particular case we create one WSListener per each connection */
/* Which may be redundant in many cases */
socket.setListener(std::make_shared<WSListener>());
}
I |2021-12-01 14:35:37 1638340537126253| MyApp:Server running on port 8000
D |2021-12-01 14:35:39 1638340539806813| Server_WSInstanceListener:New Incoming Connection. Connection count=1
D |2021-12-01 14:35:39 1638340539876373| [oatpp::web::protocol::websocket::WebSocket::listen()]:Unhandled error occurred. Message='[oatpp::web::protocol::websocket::WebSocket::readFrameHeader()]: Error reading frame header'
D |2021-12-01 14:35:39 1638340539876411| Server_WSInstanceListener:Connection closed. Connection count=0
Hey @shijiantouzouyiqie ,
Looks like at this point the WS connection appears to be not fully established (and it's a bug).
So you don't want to write to the client socket directly from this method. Instead you can use a separate thread:
void WSInstanceListener::onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr<const ParameterMap>& params) {
SOCKETS ++;
OATPP_LOGD(TAG, "New Incoming Connection. Connection count=%d", SOCKETS.load());
/* In this particular case we create one WSListener per each connection */
/* Which may be redundant in many cases */
socket.setListener(std::make_shared<WSListener>());
std::thread t([&socket]{
socket.sendOneFrameText("Hello");
});
t.detach();
}
Please file an issue on Github. WS Connection should be fully established by the time WSInstanceListener::onAfterCreate
method is called.
ENDPOINT("GET", "ws", ws, REQUEST(std::shared_ptr<IncomingRequest>, request)) {
return oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), websocketConnectionHandler);
};
I tried to make MySecurApiCall Automatic Retries by following the doc:
OATPP_CREATE_COMPONENT(std::shared_ptr<MySecureApiCall>, mySecureApiClient)([] {
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, connectionProvider, "clientConnectionProvider");
OATPP_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, objectMapper);
// create connection pool
auto connectionPool = oatpp::network::ClientConnectionPool::createShared(
connectionProvider, // connection provider
10, //max connections
std::chrono::seconds(5) // max lifetime of idle connection
);
// create retry policy
auto retryPolicy = oatpp::web::client::SimpleRetryPolicy(2 /* max retries */, std::chrono::seconds(1) /* retry interval */);
//auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionProvider);
auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionPool, retryPolicy /* retry policy */);
return MySecureApiCall::createShared(requestExecutor, objectMapper);
}());
I have modified some lines of the example, but still have "no matching function for call compile" error for "auto requestExecutor = oatpp.... " line. Please tell me what was wrong. Thank you for your help in advance.
retryPolicy
should be shared_ptr
- https://github.com/oatpp/oatpp/blob/master/src/oatpp/web/client/HttpRequestExecutor.hpp#L115
Hello @mmn:matrix.org ,
What is the canonical way to check whether a DTO field was "filled" during a deserialisation (using oatpp::parser::json::mapping::ObjectMapper)?
At the moment it's the following:
ENDPOINT("POST", "users", createUser,
BODY_DTO(Object<UserDto>, userDto))
{
OATPP_ASSERT_HTTP(userDto->id, Status::CODE_400, "id field is required")
OATPP_ASSERT_HTTP(userDto->role, Status::CODE_400, "role field is required")
...
}
Or, you can move those checks ex.: to UserDto:
class UserDto : oatpp::DTO {
...
validate() {
OATPP_ASSERT_HTTP(userDto->id, Status::CODE_400, "id field is required")
OATPP_ASSERT_HTTP(userDto->role, Status::CODE_400, "role field is required")
}
};
...
ENDPOINT("POST", "users", createUser,
BODY_DTO(Object<UserDto>, userDto))
{
userDto->validate();
...
}
Thank you for the quick response. I have modified my code as follows:
OATPP_CREATE_COMPONENT(std::shared_ptr<MySecureApiCall>, mySecureApiClient)([] {
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, connectionProvider, "clientConnectionProvider");
OATPP_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, objectMapper);
// create connection pool
auto connectionPool = std::make_shared<oatpp::network::ClientConnectionPool>(
connectionProvider, // connection provider
10, //max connections
std::chrono::seconds(5) // max lifetime of idle connection
);
// create retry policy
//auto retryPolicy = oatpp::web::client::SimpleRetryPolicy(2 /* max retries */, std::chrono::seconds(1) /* retry interval */);
auto retryPolicy = std::make_shared<oatpp::web::client::SimpleRetryPolicy>(2 /* max retries */, std::chrono::seconds(1) /* retry interval */);
//auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionProvider);
auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionPool, retryPolicy /* retry policy */);
return MySecureApiCall::createShared(requestExecutor, objectMapper);
}());
but still showed the following compile error:
AppComponent.hpp:145:9: required from here
/usr/include/c++/7/ext/new_allocator.h:136:4: error: no matching function for call to ‘oatpp::provider::Pool<oatpp::network::ClientConnectionProvider, oatpp::data::stream::IOStream, oatpp::network::ConnectionAcquisitionProxy>::Pool(std::shared_ptr<oatpp::network::ClientConnectionProvider>&, int, std::chrono::duration<long int>)’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And finally, is there some recommended way to perform a partial deserialisation? Let's say that I have a json object with one value determining the nature of the rest of its contents, so first I need to extract that one value only, based on that value select the correct DTO, and only after that deserialise the whole object with the correct DTO.
You can use Polymorphic DTO fields for that - https://github.com/oatpp/oatpp/blob/master/changelog/1.3.0.md#polymorphic-dto_field
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD(String, dataType); //<-- value determining the nature of the rest of its contents
DTO_FIELD(Any, data); //<-- polymorphic data
DTO_FIELD_TYPE_SELECTOR(data) {
if(dataType == "animal") return Object<AnimalDto>::Class::getType();
if(dataType == "car") return Object<CarDto>::Class::getType();
if(dataType == "int") return Int32::Class::getType();
return Void::Class::getType();
}
};
@mmn:matrix.org
@junyama
auto connectionPool = oatpp::network::ClientConnectionPool::createShared(
connectionProvider, // connection provider
10, //max connections
std::chrono::seconds(5) // max lifetime of idle connection
);
use ClientConnectionPool::createShared
instead
dataType
, I would need to select a whole DTO rather than a type for one field.
dataType
and in the second round for all fields with the correct DTO, but of course I'd prefer something that would at least parse the json object only once (and map twice).
Hi, I am creating async listeners with the following code:
OATPP_COMPONENT(std::shared_ptr<std::list<std::shared_ptr<oatpp::network::ServerConnectionProvider>>>, connectionProviders);
std::list<std::thread> threads;
for(auto& provider : *connectionProviders) {
threads.push_back(std::thread([provider]{
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, handler);
oatpp::network::Server server(provider, handler);
server.run();
}));
}
std::thread statThread([]{
printStats();
});
My question is: How do I properly shut the listeners down?
@mmn:matrix.org
So based on the value ofdataType
, I would need to select a whole DTO rather than a type for one field.
The obvious dirty solution is to deserialise the received json object twice, in the first round only fordataType
and in the second round for all fields with the correct DTO, but of course I'd prefer something that would at least parse the json object only once (and map twice).
Yes, in this case, you just have to parse and map it twice.
1.3.0
but API is almost the same
I have set RetryPolicy as follows:
std::unordered_set<v_int32> m_httpCodes{500};
auto retryPolicy = std::make_shared<oatpp::web::client::SimpleRetryPolicy>(10, std::chrono::seconds(1), m_httpCodes);
Looking at the log of my client code, I saw some strange HTTP response status code, such as 3 and 4. My ENDPOINT is based on CRUD example:
ENDPOINT("POST", "images", createImage, BODY_DTO(Object<ImageDto>, imageDto)) {
auto userDto = UserDto::createShared();
serializeImageDto(imageDto, userDto);
return createDtoResponse(Status::CODE_200, m_imageServices.createUser(userDto));
}
I do not change UserService.cpp. What was wrong? Thank you for your help in advance.
@oatpp_io_twitter
Hello, question: when the server takes the initiative to send data, F5 refreshes the browser page. The server suddenly reports an error. What's the better way?
Thank you. Here is an example:
class WSInstanceListener : public oatpp::websocket::ConnectionHandler::SocketInstanceListener {
public:
static bool m_bState;
...
};
bool WSInstanceListener::m_bState = true;
void WSInstanceListener::onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr<const ParameterMap>& params) {
SOCKETS ++;
OATPP_LOGD(TAG, "New Incoming Connection. Connection count=%d", SOCKETS.load());
/* In this particular case we create one WSListener per each connection */
/* Which may be redundant in many cases */
socket.setListener(std::make_shared<WSListener>());
m_bState = true;
std::thread t([&socket]{
while (m_bState) {
socket.sendOneFrameText("Hello");
}
});
t.detach();
}
void WSInstanceListener::onBeforeDestroy(const oatpp::websocket::WebSocket& socket) {
SOCKETS --;
OATPP_LOGD(TAG, "Connection closed. Connection count=%d", SOCKETS.load());
m_bState = false;
}
I |2021-12-09 18:45:59 1639043159419263| MyApp:Server running on port 8000
D |2021-12-09 18:46:06 1639043166551007| Server_WSListener:onClose code=1001
D |2021-12-09 18:46:06 1639043166551045| Server_WSInstanceListener:Connection closed. Connection count=0
terminate called after throwing an instance of 'std::runtime_error'
what(): [oatpp::web::protocol::websocket::WebSocket::sendOneFrameText()]: Unknown error while writing to socket.
Hello, question: when the server takes the initiative to send data, F5 refreshes the browser page. The server suddenly reports an error. What's the better way?
Hey @shijiantouzouyiqie , if you refresh the page in web-browser then the client is disconnected from the server, and the server reports that it can't use that already invalid connection.
Hi I was wondering how to deserialize the following using oatpp::DTO:
"changes": [
[
"buy",
"9122.04",
"0.00121425"
],
...,
[
"sell",
"9122.07",
"0.98942292"
]
...
],
I had hoped I'd be able to do something like this:Vector<Object<Change>>
as opposed to Vector<Vector<String>>>
. Ideas?
Also wondering how to do something similar for this:
"trades": [
{
"type": "trade",
"symbol": "BTCUSD",
"eventid": 169841458,
"timestamp": 1560976400428,
"price": "9122.04",
"quantity": "0.0073173",
"side": "sell"
},
...
],
Thanks.