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 {
73 std::size_t operator()(
HandlerErrorCode c)
const {
return static_cast<std::size_t>(c); }
76using Headers = std::unordered_map<std::string, std::string, utils::StrIcaseHash, utils::StrIcaseEqual>;
78std::string_view GetCodeDescription(HandlerErrorCode);
80std::string_view GetFallbackServiceCode(HandlerErrorCode);
101using IsExternalBodyFormatted = std::bool_constant<T::kIsExternalBodyFormatted>;
104using HasServiceCode =
decltype(std::declval<
const T&>().GetServiceCode());
107using HasInternalMessage =
decltype(std::declval<
const T&>().GetInternalMessage());
110inline constexpr bool kHasInternalMessage = meta::kIsDetected<HasInternalMessage, T>;
113using HasExternalBody =
decltype(std::declval<
const T&>().GetExternalBody());
116inline constexpr bool kHasExternalBody = meta::kIsDetected<HasExternalBody, T>;
119inline constexpr bool kIsMessageBuilder = kHasExternalBody<T>;
122struct MessageExtractor {
124 meta::kIsDetected<HasExternalBody, T>,
125 "Please use your message builder to build external body for "
126 "your error. See server::handlers::CustomHandlerException "
132 constexpr bool IsExternalBodyFormatted()
const {
133 return meta::DetectedOr<std::false_type, impl::IsExternalBodyFormatted, T>::value;
136 std::string GetServiceCode()
const {
137 if constexpr (meta::kIsDetected<HasServiceCode, T>) {
138 return builder.GetServiceCode();
140 return std::string{};
144 std::string GetExternalBody()
const {
return builder.GetExternalBody(); }
146 std::string GetInternalMessage()
const {
147 if constexpr (kHasInternalMessage<T>) {
148 return builder.GetInternalMessage();
150 return std::string{};
155struct CustomHandlerExceptionData
final {
156 CustomHandlerExceptionData() =
default;
157 CustomHandlerExceptionData(
const CustomHandlerExceptionData&) =
default;
158 CustomHandlerExceptionData(CustomHandlerExceptionData&&)
noexcept =
default;
160 template <
typename... Args>
161 explicit CustomHandlerExceptionData(Args&&... args) {
162 (Apply(std::forward<Args>(args)), ...);
165 bool is_external_body_formatted{
false};
167 std::string service_code;
168 std::string internal_message;
169 std::string external_body;
174 void Apply(
HandlerErrorCode handler_code_) { handler_code = handler_code_; }
176 void Apply(
ServiceErrorCode service_code_) { service_code = std::move(service_code_.body); }
178 void Apply(
InternalMessage internal_message_) { internal_message = std::move(internal_message_.body); }
180 void Apply(
ExternalBody external_body_) { external_body = std::move(external_body_.body); }
182 void Apply(
ExtraHeaders headers_) { headers = std::move(headers_.headers); }
184 void Apply(
formats::
json::Value details_) { details = std::move(details_); }
186 template <
typename MessageBuilder>
187 void Apply(MessageBuilder&& builder) {
188 impl::MessageExtractor<MessageBuilder> extractor{builder};
189 is_external_body_formatted = extractor.IsExternalBodyFormatted();
190 service_code = extractor.GetServiceCode();
191 external_body = extractor.GetExternalBody();
192 internal_message = extractor.GetInternalMessage();
242 template <
typename... Args>
252 ServiceErrorCode service_code,
253 ExternalBody external_body,
254 InternalMessage internal_message,
255 HandlerErrorCode handler_code,
256 ExtraHeaders headers = {},
270 :
CustomHandlerException(impl::CustomHandlerExceptionData{std::forward<MessageBuilder>(builder), handler_code}
274 explicit CustomHandlerException(impl::CustomHandlerExceptionData&& data)
276 data.internal_message.empty() ? std::string{GetCodeDescription(data.handler_code)} : data.internal_message
278 data_(std::move(data)) {
280 data_.details.IsNull() || data_.details.IsObject(),
281 "The details JSON value must be either null or an object"
286 HandlerErrorCode GetCode()
const {
return data_.handler_code; }
288 const std::string& GetServiceCode()
const {
return data_.service_code; }
290 bool IsExternalErrorBodyFormatted()
const {
return data_.is_external_body_formatted; }
292 const std::string& GetExternalErrorBody()
const {
return data_.external_body; }
294 const formats::
json::Value& GetDetails()
const {
return data_.details; }
296 const Headers& GetExtraHeaders()
const {
return data_.headers; }
299 impl::CustomHandlerExceptionData data_;
308 constexpr static HandlerErrorCode kDefaultCode = Code;
315 template <
typename... Args>