changelog/1.4.0.md
Previous release - 1.3.0
Feel free to ask questions - Chat on Gitter!
Contents:
Oat++ 1.4.0 requires C++ 17.
#include "oatpp/encoding/Url.hpp"
...
oatpp::String data = "Hello URL-Encoder!!!";
oatpp::encoding::Url::Config config;
auto encoded = oatpp::encoding::Url::encode(data, config);
auto decoded = oatpp::encoding::Url::decode(encoded);
OATPP_ASSERT(decoded == data)
Note: Oat++ does NOT automatically decode URL and its parameters on endpoint hit.
#include "oatpp/async/ConditionVariable.hpp"
...
oatpp::async::Lock* m_lock;
oatpp::async::ConditionVariable* m_cv;
...
Action act() override {
return m_cv->waitFor(m_lock, // async::Lock
[this]{return m_resource->counter == 100;}, // condition
std::chrono::seconds(5)) // timeout
.next(finish());
}
...
New mapping-enabled type oatpp::Tree for flexible data access.
ENDPOINT("POST", "users", createUser,
BODY_DTO(oatpp::Tree, user))
{
oatpp::String name = user["name"];
v_uint16 age = user["age"];
auto& subs = user["subscriptions"].getVector();
for(auto& s : subs) {
...
}
...
}
Any node of oatpp::Tree can be mapped to DTO or to any mapping-enabled oatpp type - see Remapper
oatpp::data::mapping::Remapper.
Remapper can be user to remap any oatpp type to any oatpp type. UnorderedFields can be mapped to DTOs, DTOs to vectors of values, vector items to other DTOs, DTOs to Trees, etc...
class User : public oatpp::DTO {
DTO_INIT(User, DTO)
DTO_FIELD(String, name);
DTO_FIELD(UInt32, age);
};
...
oatpp::data::mapping::Remapper remapper;
auto user = User::createShared();
user->name = "Jane";
user->age = "25";
auto tree = remapper.remap<oatpp::Tree>(user); // remap to tree
auto fields = remapper.remap<oatpp::Fields<oatpp::Tree>>(user); // remap to Fields
auto otherDto = remapper.remap<oatpp::Object<OtherDto>>(user); // remap to OtherDto
auto values = remapper.remap<oatpp::Vector<oatpp::Tree>>(user); // remap to Vector
oatpp::String name = values[0];
v_uint32 age = values[1];
Now ApiController can be provided with ContentMappers object for automatic DTO mapper selection based
on Content-Type/Accept headers.
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::web::mime::ContentMappers>, apiContentMappers)([] {
auto mappers = std::make_shared<oatpp::web::mime::ContentMappers>();
mappers->putMapper(std::make_shared<oatpp::json::ObjectMapper>()); // add json mapper
mappers->putMapper(std::make_shared<oatpp::xml::ObjectMapper>()); // add xml mapper (NOT released yet)
return mappers;
}());
UserController(OATPP_COMPONENT(std::shared_ptr<oatpp::web::mime::ContentMappers>, contentMappers))
: oatpp::web::server::api::ApiController(contentMappers)
{}
ENDPOINT("POST", "users", createUser,
BODY_DTO(Object<UserDto>, userDto), // UserDto mapped according to 'Content-Type' header
REQUEST(std::shared_ptr<IncomingRequest>, request))
{
return createDtoResponse(Status::CODE_200,
m_userService.createUser(userDto),
request->getHeaderValues("Accept")); // response DTO mapped according to 'Accept' header
}
Now oatpp logs are type-safe. Also log formatting changed.
| old logs | new logs |
|---|---|
OATPP_LOGV | OATPP_LOGv |
OATPP_LOGD | OATPP_LOGd |
OATPP_LOGI | OATPP_LOGi |
OATPP_LOGW | OATPP_LOGw |
OATPP_LOGE | OATPP_LOGe |
Instead of old formatting "%s", "%d", "%f" use "{}" for any variable type:
OATPP_LOGd("MyController", "User: name={}, age={}", user->name, user->age)
Fixed error handling in the ApiController and ConnectionHandler.
The new recommended way to implement custom error handler:
/**
* Extend the DefaultErrorHandling class by overriding the renderError() method instead of the handleError() method.
* You can still override the handleError() method, but in most cases, it isn't necessary.
*/
class ErrorHandler : public oatpp::web::server::handler::DefaultErrorHandler {
private:
/* mappers to map response according to 'Accept' header and available mappers */
std::shared_ptr<oatpp::web::mime::ContentMappers> m_mappers;
public:
ErrorHandler(const std::shared_ptr<oatpp::web::mime::ContentMappers>& mappers)
: m_mappers(mappers)
{}
std::shared_ptr<OutgoingResponse> renderError(const HttpServerErrorStacktrace& stacktrace) override {
/* create ErrorDto */
auto error = ErrorDto::createShared();
error->code = stacktrace.status.code;
error->stack = {}; // initialize stack as empty list
/* push all items of stacktrace */
for(auto& s : stacktrace.stack) {
error->stack->push_back(s);
}
/* get all variants of acceptable mime-types from request */
std::vector<oatpp::String> acceptable;
if(stacktrace.request) {
acceptable = stacktrace.request->getHeaderValues("Accept");
}
/* select mapper based on provided preferences */
auto mapper = m_mappers->selectMapper(acceptable);
if(!mapper) {
mapper = m_mappers->getDefaultMapper();
}
/* create response object */
auto response = ResponseFactory::createResponse(stacktrace.status, error, mapper);
/* put all error related headers */
for(const auto& pair : stacktrace.headers.getAll()) {
response->putHeader(pair.first.toString(), pair.second.toString());
}
/* return response */
return response;
}
};
| old file | new file |
|---|---|
oatpp/parser/json/* | oatpp/json/* |
oatpp/parser/json/mapping/* | oatpp/json/* |
oatpp/algorithm/CRC.hpp | oatpp/utils/CRC32.hpp |
oatpp/core/utils/* | oatpp/utils/* |
oatpp/core/utils/ConversionUtils.hpp | oatpp/utils/Conversion.hpp |
oatpp/core/macro/* | oatpp/macro/* |
oatpp/core/async/* | oatpp/async/* |
oatpp/core/Types.hpp | oatpp/Types.hpp |
oatpp/core/IODefinitions.hpp | oatpp/IODefinitions.hpp |
oatpp/core/base/Environment.hpp | oatpp/Environment.hpp |
oatpp/core/base/* | oatpp/base/* |
oatpp/core/concurrency/* | oatpp/concurrency/* |
oatpp/core/provider/* | oatpp/provider/* |
oatpp/core/data/* | oatpp/data/* |
oatpp/core/parser/* | oatpp/utils/parser/* |
oatpp/data/mapping/type/* | oatpp/data/type/* |
| old namespace | new namespace |
|---|---|
oatpp::parser::json::* | oatpp::json::* |
oatpp::parser::json::mapping::* | oatpp::json::* |
oatpp::algorithm::CRC | oatpp::utils::CRC32 |
oatpp::data::mapping::type::* | oatpp::data::type::* |