I'm trying to achieve very simple thing.
Say, I have a REST API. When I call
/api/recipe/1
I'd like to a resource as a json to be returned.
When I hit
/api/recipe/2
a 404 Not Found HTTP response should be returned. Simple as that.
Clearly I'm missing something about how routing directives work, as I'm not able to compose them to respect above-mentioned logic.
Unfortunately I was unable to find any concrete example and official documentation isn't particularly helpful.
I'm trying something like this but the code gives compilation error:
class RecipeResource(recipeService: RecipeService)(implicit executionContext: ExecutionContext) extends DefaultJsonProtocol {
implicit val recipeFormat = jsonFormat1(Recipe.apply)
val routes = pathPrefix("recipe") {
(get & path(LongNumber)) { id =>
complete {
recipeService.getRecipeById(id).map {
case Some(recipe) => ToResponseMarshallable(recipe)
// type mismatch here, akka.http.scaladsl.marshalling.ToResponseMarshallable
// is required
case None => HttpResponse(StatusCodes.NotFound)
}
}
}
}
}
Here's the code of recipeService
for greater clarity:
class RecipeService(implicit executionContext: ExecutionContext) {
def getRecipeById(id: Long): Future[Option[Recipe]] = {
id match {
case 1 => Future.successful(Some(Recipe("Imperial IPA")))
case _ => Future.successful(None)
}
}
}
The compilation error I get:
[error] /h......../....../...../RecipeResource.scala:22: type mismatch;
[error] found : scala.concurrent.Future[Object]
[error] required: akka.http.scaladsl.marshalling.ToResponseMarshallable
[error] recipeService.getRecipeById(id).map {
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
Based on leachbj's answer I got rid of needless pattern matching in the route. Now the code compiles and looks like this:
class RecipeResource(recipeService: RecipeService)(implicit executionContext: ExecutionContext) extends DefaultJsonProtocol {
implicit val recipeFormat = jsonFormat1(Recipe.apply)
val routes = pathPrefix("recipe") {
(get & path(LongNumber)) { id =>
complete(recipeService.getRecipeById(id))
}
}
}
When the recipe exists (e.g. /api/recipe/1
) I get the JSON response and 200 OK
, which is expected.
Now, in case of the non-existent resource (e.g. /api/recipe/2
) the response in empty, but 200 OK
status code is received.
My question is, how can I tweak akka-http to be able to complete(Future[None[T]])
that would return 404 Not found
.
I'm looking for a generic approach that would work for any Future[None]
return value.
If you complete(Future[Option[T]])
and there is a suitable Json Marshaller available the Akka will return the response as json if the value is Some(v)
or an empty 200 response for None
. If you use spray-json create a RootJsonFormat[T]
implicit and add import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
. There are similar support implicits for other marshalling libraries.
To generate a 404 for None
you need to wrap the complete
with the rejectEmptyResponse
directive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With