Цирк Http4s не может декодировать детей
У меня есть модель ошибки, например:
sealed trait HttpError {
val msg: String
val cause: String
}
final case class HttpDecodingError(cause: String) extends HttpError {
override val msg: String = "Decoding error"
}
final case class HttpInternalServerError(msg: String, cause: String) extends HttpError
case object HttpUnauthorizedError extends HttpError {
override val msg: String = "Invalid credentials"
override val cause: String = ""
}
final case class HttpBadRequestError(msg: String, cause: String) extends HttpError
на моем маршруте я генерирую тип ошибки http на основе этой модели, т.е.:
.foldM(
{
case error: HttpDecodingError => BadRequest(error.asInstanceOf[HttpError])
case error: HttpInternalServerError => InternalServerError(error.asInstanceOf[HttpError])
case HttpUnauthorizedError => Unauthorized(withChallenge("Invalid credentials"))
case error: HttpBadRequestError => BadRequest(error.asInstanceOf[HttpError])
},
Ok(_)
)
но проблема в том, что мне нужно добавить это asInstanceOf, иначе circe не видит энкодер. Мой кодировщик выглядит так:
implicit val encodeHttpError: Encoder[HttpError] = (error: HttpError) =>
Json.obj(("msg", Json.fromString(error.msg)), ("cause", Json.fromString(error.cause)))
есть ли способ избежать выполнения asInstanceOf там?
1 ответ
Вы не можете использовать кодировщик для его подклассов, потому что
Encoder
является инвариантным (это сработало бы, если бы оно было ковариантным).
Одним из решений, которое вы могли бы использовать, является определение кодировщика с использованием параметризованного
def
вместо
val
:
implicit def encodeHttpError[E <: HttpError]: Encoder[E] = (error: E) =>
Json.obj(
("msg", Json.fromString(error.msg)),
("cause", Json.fromString(error.cause))
)
Таким образом, у вас будет экземпляр кодировщика для всех подтипов, а также для
HttpError
.