oatpp-authkit/include/oatpp-authkit/handler/JsonErrorHandler.hpp
Uwe Schuster f43f5f0633 #6: route ad-hoc JSON through ObjectMapper (Option A — DI everywhere, all-in-one)
- New dto/InternalDto.hpp with JsonErrorDto, WsEntityEventDto,
  WsPresenceUpdateDto, WsClientMsgDto.

- JsonErrorHandler: now takes a shared ObjectMapper (DI). Body built
  via writeToString on JsonErrorDto. Closes the audit's concrete bug
  where status.description was embedded raw — a Status with a `"`/`\\`
  in the description previously emitted invalid JSON.

- AuthInterceptor: takes an optional ObjectMapper ctor arg (defaults to
  a fresh mapper). makeForbidden's `msg` is now serialised via
  JsonErrorDto + ObjectMapper, so a `"` in a forbidden-reason no longer
  breaks the response envelope.

- Hub: process-wide sharedMapper() with optional setObjectMapper()
  override. buildPresenceMsg / notifyBooking / notifyPerson all go
  through ObjectMapper-emitted DTOs. User-supplied IDs / property IDs
  / usernames containing `"`/`\\`/control chars are now escaped.

- Listener: jsonStr/jsonInt regex parsers gone. handleMessage parses
  inbound frames via ObjectMapper::readFromString into WsClientMsgDto.
  Malformed JSON / nested objects / escaped quotes — previously silent
  corruption — now produce a clean drop of the frame.

- test/test_json_serialization.cpp: 4 cases pinning the round-trip
  behaviour (special chars in usernames, IDs, status.description, and
  malformed-input rejection).

Bump to 0.4.0 — ctor signatures changed (additive defaults, but the
behaviour of the JSON envelopes is now governed by ObjectMapper).

Closes #6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:56:05 +02:00

62 lines
2.2 KiB
C++

#ifndef HANDLER_JSON_ERROR_HANDLER_HPP
#define HANDLER_JSON_ERROR_HANDLER_HPP
#include "oatpp/web/server/handler/ErrorHandler.hpp"
#include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "../dto/InternalDto.hpp"
namespace oatpp_authkit {
/**
* @brief Custom error handler that returns JSON error responses.
*
* Replaces oatpp's default plain-text error handler so that
* OATPP_ASSERT_HTTP errors are returned as JSON objects matching the
* `JsonErrorDto` schema: `{"status": "...", "code": N, "message": "..."}`.
*
* Routing through `ObjectMapper` (DI'd) replaces the previous hand-rolled
* concatenation that embedded `status.description` raw — see #6.
*/
class JsonErrorHandler : public oatpp::web::server::handler::ErrorHandler {
private:
std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_mapper;
public:
/**
* @param mapper Shared JSON object mapper. Pass nullptr for a
* handler-owned default mapper (back-compat path).
*/
explicit JsonErrorHandler(std::shared_ptr<oatpp::data::mapping::ObjectMapper> mapper = nullptr)
: m_mapper(mapper ? mapper : oatpp::parser::json::mapping::ObjectMapper::createShared()) {}
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
handleError(const oatpp::web::protocol::http::Status& status,
const oatpp::String& message,
const Headers& headers) override
{
auto dto = dto::JsonErrorDto::createShared();
dto->status = oatpp::String(std::string(status.description));
dto->code = status.code;
dto->message = message ? message : oatpp::String("");
oatpp::String json = m_mapper->writeToString(dto);
auto response = oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse(
status, json
);
response->putHeader("Content-Type", "application/json");
for (const auto& pair : headers.getAll()) {
response->putHeader(pair.first.toString(), pair.second.toString());
}
return response;
}
};
} // namespace oatpp_authkit
#endif // HANDLER_JSON_ERROR_HANDLER_HPP