-
Notifications
You must be signed in to change notification settings - Fork 423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to handle an error with fs2.Stream based endpoint #3940
Comments
I think the problem is that the error is only returned as part of a stream. So: you are always returning a However, it's already too late to change how the response is generated, as we've already chosen the successful output. Note that in theory, such a stream error might occur also when e.g. half of the avatar is already transmitted. The proper solution would be to change the signature of the |
Here's a working example, using import cats.effect.*
import cats.syntax.all.*
import io.circe.generic.auto.*
import org.http4s.HttpRoutes
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.server.Router
import sttp.capabilities.fs2.Fs2Streams
import sttp.model.{MediaType, StatusCode}
import sttp.tapir.*
import sttp.tapir.generic.auto.*
import sttp.tapir.json.circe.jsonBody
import sttp.tapir.server.http4s.Http4sServerInterpreter
import scala.concurrent.ExecutionContext
object HelloWorldHttp4sServer extends IOApp:
sealed class AvatarError extends Exception
case object UnknownAvatar extends AvatarError
def avatar[F[_]] =
endpoint.get
.in("user" / path[String] / "avatar.png")
.errorOut(
oneOf[AvatarError](
oneOfVariant(
statusCode(StatusCode.NotFound)
.and(jsonBody[UnknownAvatar.type].description("no avatar found for user"))
)
)
)
.out(streamBinaryBody(Fs2Streams[F])(new CodecFormat {
override def mediaType: MediaType = MediaType.ImagePng
}))
def getAvatar(id: String): IO[fs2.Stream[IO, Byte]] =
if id == "ok" then IO.pure(fs2.Stream.fromIterator[IO]("picture".getBytes.iterator, 16)) else IO.raiseError(UnknownAvatar)
def avatarSEP = avatar.serverLogic { id =>
val value = getAvatar(id)
value
.map(stream => Right(stream))
.recoverWith { case UnknownAvatar =>
IO(println("error encountered")).map(_ => Left(UnknownAvatar))
}
}
val helloWorldRoutes: HttpRoutes[IO] = Http4sServerInterpreter[IO]().toRoutes(avatarSEP)
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
override def run(args: List[String]): IO[ExitCode] =
// starting the server
BlazeServerBuilder[IO]
.withExecutionContext(ec)
.bindHttp(8080, "localhost")
.withHttpApp(Router("/" -> helloWorldRoutes).orNotFound)
.resource
.use { _ => IO.never }
.as(ExitCode.Success) |
Hi,
I am struggling with adding an error out to a stream based endpoint. I want to return a 404 when the Stream has a
UnknownAvatar
error (inside thegetAvatar
i am adapting anIOException
toUnknownAvatar
)Here's the endpoint definition:
The endpoint implementation:
This does not work and a left value is still returned to the client. Resulting in a "Connection prematurely closed DURING response". The only debug message i see is the first one.
Thanks so much for any help!
The text was updated successfully, but these errors were encountered: