As I understood your question,
...I don't know how to deal with the IllegalArgumentException thrown when the ID is wrong!
I think, the better solution is
def getById(id: String)(implicit ec: ExecutionContext): Future[Option[SomeModel]]={
//Try to parse bson id from string. This method return Try[BSONObjectId] and we can simple `match` them
BSONObjectId.parse(id) match {
// valid bson id
case Success(bsonId) => this.get( BSONDocument("_id" -> bsonId) )
//We catch IllegalArgumentException and just return None
case Failure(ex) => Future[Option[SomeModel]](None)
}
}
In your code, Scala try to parse BSONObjectId from string before call get
method, and if string id is invalid BSON throwing exception in current thread (not in the Future
result of method get
). That's why recover {case _ => Future.successful(None)}
will not execute. Methods recover
and recoverWith
executes only if Future
store some exception. For example, this code will work too:
def getById(id: String)(implicit ec: ExecutionContext): Future[Option[SomeModel]]={
//create Future, that will be store exception (if id is invalid) or valid BSON id.
//method flatMap because this.get return Future type.
Future(BSONObjectId(id)).flatMap{ bsonId =>
//this executes only if string id is valid bson.
this.get( BSONDocument("_id" -> bsonId) )
}.recover{
//this will be execute only if string id is invalid bson.
// the best practice to catch non-fatal Throwables via class scala.util.control.NonFatal
case NonFatal(e) => None
}
}
But this variant is complicated (create one more Future
, flatMap
them, recover with NonFatal
control). I would prefer the first variant with parse
method (it's mush more easy without some extra futures and controls).