|
@@ -9,6 +9,9 @@ Contents:
|
|
|
- [The New oatpp::String](#the-new-oatppstring)
|
|
|
- [ConnectionPool::get() Timeout](#connectionpoolget-timeout)
|
|
|
- [JSON Serializer Escape Flags](#json-serializer-escape-flags)
|
|
|
+- [Headers Stored In unordered_multimap](#headers-stored-in-unordered_multimap)
|
|
|
+- [QueryParameters Stored In unordered_multimap](#queryparameters-stored-in-unordered_multimap)
|
|
|
+- [Polymorphic DTO_FIELD](#polymorphic-dto_field)
|
|
|
- [ConnectionMonitor](#connectionmonitor)
|
|
|
- [Request Data Bundle](#request-data-bundle)
|
|
|
- [ConnectionProviderSwitch](#connectionproviderswitch)
|
|
@@ -146,6 +149,137 @@ Output:
|
|
|
res='"https://oatpp.io/"' # solidus isn't escaped
|
|
|
```
|
|
|
|
|
|
+## Headers Stored In unordered_multimap
|
|
|
+
|
|
|
+Headers are now stored using [std::unordered_multimap](https://en.cppreference.com/w/cpp/container/unordered_multimap).
|
|
|
+
|
|
|
+Put multiple headers:
|
|
|
+
|
|
|
+```cpp
|
|
|
+auto response = createResponse(Status::CODE_200, "");
|
|
|
+response->putHeader("Set-Cookie", "...");
|
|
|
+response->putHeader("Set-Cookie", "...");
|
|
|
+return response;
|
|
|
+```
|
|
|
+
|
|
|
+Log all "Set-Cookie" headers:
|
|
|
+
|
|
|
+```cpp
|
|
|
+ const auto& map = headers.getAll();
|
|
|
+ auto bucket = map.bucket("Set-Cookie");
|
|
|
+ auto bucketBegin = map.begin(bucket);
|
|
|
+ auto bucketEnd = map.end(bucket);
|
|
|
+
|
|
|
+ for(auto it = bucketBegin; it != bucketEnd; it ++) {
|
|
|
+ oatpp::String value = it->second.toString();
|
|
|
+ OATPP_LOGD("Header", "Set-Cookie: %s", value->c_str());
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+## QueryParameters Stored In unordered_multimap
|
|
|
+
|
|
|
+QueryParameters are now stored using [std::unordered_multimap](https://en.cppreference.com/w/cpp/container/unordered_multimap).
|
|
|
+
|
|
|
+Log all entries of "userId" query parameter:
|
|
|
+
|
|
|
+```cpp
|
|
|
+ const auto& map = request->getQueryParameters().getAll();
|
|
|
+ auto bucket = map.bucket("userId");
|
|
|
+ auto bucketBegin = map.begin(bucket);
|
|
|
+ auto bucketEnd = map.end(bucket);
|
|
|
+
|
|
|
+ for(auto it = bucketBegin; it != bucketEnd; it ++) {
|
|
|
+ oatpp::String value = it->second.toString();
|
|
|
+ OATPP_LOGD("QueryParameter", "userId: %s", value->c_str());
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+## Polymorphic DTO_FIELD
|
|
|
+
|
|
|
+Now, when used inside of a DTO, we can specify exact types that `oatpp::Any` can store by specifying `DTO_FIELD_TYPE_SELECTOR`:
|
|
|
+
|
|
|
+```cpp
|
|
|
+/* Possible type of a DTO_FIELD */
|
|
|
+class ClassA : public oatpp::DTO {
|
|
|
+
|
|
|
+ DTO_INIT(ClassA, DTO)
|
|
|
+
|
|
|
+ DTO_FIELD(String, value);
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+/* Possible type of a DTO_FIELD */
|
|
|
+class ClassB : public oatpp::DTO {
|
|
|
+
|
|
|
+ DTO_INIT(ClassB, DTO)
|
|
|
+
|
|
|
+ DTO_FIELD(Vector<String>, values);
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+/* enum of possible DTO_FIELD types */
|
|
|
+ENUM(ClassType, v_int32,
|
|
|
+ VALUE(CLASS_TYPE_A, 0),
|
|
|
+ VALUE(CLASS_TYPE_B, 1)
|
|
|
+)
|
|
|
+
|
|
|
+/* our DTO */
|
|
|
+class ResponseDto : public oatpp::DTO {
|
|
|
+
|
|
|
+ DTO_INIT(ResponseDto, DTO)
|
|
|
+
|
|
|
+ /* type control field */
|
|
|
+ DTO_FIELD(Enum<ClassType>::AsString, payloadType);
|
|
|
+
|
|
|
+ /* polymorphic field */
|
|
|
+ DTO_FIELD(Any, payload);
|
|
|
+
|
|
|
+ /* type selector */
|
|
|
+ DTO_FIELD_TYPE_SELECTOR(payload) {
|
|
|
+ if(!payloadType) return Void::Class::getType();
|
|
|
+ switch (*payloadType) {
|
|
|
+ case ClassType::CLASS_TYPE_A: return Object<ClassA>::Class::getType();
|
|
|
+ case ClassType::CLASS_TYPE_B: return Object<ClassB>::Class::getType();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+...
|
|
|
+
|
|
|
+/* send polymorphic payload to client */
|
|
|
+ENDPOINT("GET", "payload", getPayload) {
|
|
|
+
|
|
|
+ auto payload = ClassB::createShared();
|
|
|
+ payload->values = {"value1", "value2", "value3"};
|
|
|
+
|
|
|
+ auto r = ResponseDto::createShared();
|
|
|
+ r->payloadType = ClassType::CLASS_TYPE_B;
|
|
|
+ r->payload = payload;
|
|
|
+
|
|
|
+ return createDtoResponse(Status::CODE_200, r);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/* receive polymorphic payload from client */
|
|
|
+ENDPOINT("POST", "payload", postPayload,
|
|
|
+ BODY_DTO(oatpp::Object<ResponseDto>, r))
|
|
|
+{
|
|
|
+
|
|
|
+ /* check type-control field and retrieve value of the corresponding type */
|
|
|
+ if(r->payloadType == ClassType::CLASS_TYPE_B) {
|
|
|
+ auto payload = r->payload.retrieve<oatpp::Object<ClassB>>();
|
|
|
+ for(auto& value : *payload->values) {
|
|
|
+ OATPP_LOGD("VALUE", "%s", value->c_str());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return createResponse(Status::CODE_200, "OK");
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
## ConnectionMonitor
|
|
|
|
|
|
`oatpp::network::monitor::ConnectionMonitor` is a middleman who's able to monitor provided connections and close those ones that not satisfy selected rules.
|