8#include <unordered_map>
10#include <userver/formats/json.hpp>
11#include <userver/utils/assert.hpp>
12#include <userver/utils/meta_light.hpp>
13#include <userver/utils/str_icase.hpp>
14#include <userver/utils/void_t.hpp>
16USERVER_NAMESPACE_BEGIN
18namespace server::handlers {
74 return static_cast<std::size_t>(c);
78using Headers = std::unordered_map<std::string, std::string,
79 utils::StrIcaseHash, utils::StrIcaseEqual>;
81std::string_view GetCodeDescription(HandlerErrorCode);
83std::string_view GetFallbackServiceCode(HandlerErrorCode);
104using IsExternalBodyFormatted = std::bool_constant<T::kIsExternalBodyFormatted>;
107using HasServiceCode =
decltype(std::declval<
const T&>().GetServiceCode());
110using HasInternalMessage =
111 decltype(std::declval<
const T&>().GetInternalMessage());
114inline constexpr bool kHasInternalMessage =
115 meta::kIsDetected<HasInternalMessage, T>;
118using HasExternalBody =
decltype(std::declval<
const T&>().GetExternalBody());
121inline constexpr bool kHasExternalBody = meta::kIsDetected<HasExternalBody, T>;
124inline constexpr bool kIsMessageBuilder = kHasExternalBody<T>;
127struct MessageExtractor {
128 static_assert(meta::kIsDetected<HasExternalBody, T>,
129 "Please use your message builder to build external body for "
130 "your error. See server::handlers::CustomHandlerException "
135 constexpr bool IsExternalBodyFormatted()
const {
136 return meta::DetectedOr<std::false_type, impl::IsExternalBodyFormatted,
140 std::string GetServiceCode()
const {
141 if constexpr (meta::kIsDetected<HasServiceCode, T>) {
142 return builder.GetServiceCode();
144 return std::string{};
148 std::string GetExternalBody()
const {
return builder.GetExternalBody(); }
150 std::string GetInternalMessage()
const {
151 if constexpr (kHasInternalMessage<T>) {
152 return builder.GetInternalMessage();
154 return std::string{};
159struct CustomHandlerExceptionData
final {
160 CustomHandlerExceptionData() =
default;
161 CustomHandlerExceptionData(
const CustomHandlerExceptionData&) =
default;
162 CustomHandlerExceptionData(CustomHandlerExceptionData&&)
noexcept =
default;
164 template <
typename... Args>
165 explicit CustomHandlerExceptionData(Args&&... args) {
166 (Apply(std::forward<Args>(args)), ...);
169 bool is_external_body_formatted{
false};
171 std::string service_code;
172 std::string internal_message;
173 std::string external_body;
175 formats::
json::Value details;
178 void Apply(
HandlerErrorCode handler_code_) { handler_code = handler_code_; }
181 service_code = std::move(service_code_.body);
185 internal_message = std::move(internal_message_.body);
189 external_body = std::move(external_body_.body);
192 void Apply(
ExtraHeaders headers_) { headers = std::move(headers_.headers); }
194 void Apply(formats::
json::Value details_) { details = std::move(details_); }
196 template <
typename MessageBuilder>
197 void Apply(MessageBuilder&& builder) {
198 impl::MessageExtractor<MessageBuilder> extractor{builder};
199 is_external_body_formatted = extractor.IsExternalBodyFormatted();
200 service_code = extractor.GetServiceCode();
201 external_body = extractor.GetExternalBody();
202 internal_message = extractor.GetInternalMessage();
252 template <
typename... Args>
255 handler_code, std::forward<Args>(args)...}) {}
264 ExternalBody external_body,
265 InternalMessage internal_message,
266 HandlerErrorCode handler_code,
267 ExtraHeaders headers = {},
268 formats::
json::Value details = {})
276 typename MessageBuilder,
279 HandlerErrorCode handler_code)
281 std::forward<MessageBuilder>(builder), handler_code}) {}
284 explicit CustomHandlerException(impl::CustomHandlerExceptionData&& data)
285 : runtime_error(data.internal_message.empty()
286 ? std::string{GetCodeDescription(data.handler_code)}
287 : data.internal_message),
288 data_(std::move(data)) {
289 UASSERT_MSG(data_.details.IsNull() || data_.details.IsObject(),
290 "The details JSON value must be either null or an object");
294 HandlerErrorCode GetCode()
const {
return data_.handler_code; }
296 const std::string& GetServiceCode()
const {
return data_.service_code; }
298 bool IsExternalErrorBodyFormatted()
const {
299 return data_.is_external_body_formatted;
302 const std::string& GetExternalErrorBody()
const {
303 return data_.external_body;
306 const formats::
json::Value& GetDetails()
const {
return data_.details; };
308 const Headers& GetExtraHeaders()
const {
return data_.headers; }
311 impl::CustomHandlerExceptionData data_;
320 constexpr static HandlerErrorCode kDefaultCode = Code;
327 template <
typename... Args>