需要帮助处理 reactivemongo 中的 IllegalArgumentException

Need help handling IllegalArgumentException in reactivemongo

下面的代码尝试使用 Reactivemongo 按 ID 获取文档。但是不知道如何处理ID错误时抛出的IllegalArgumentException! 尝试了下面的代码,但编译器对 case _ => Future.successful(None) 不满意,它说:found scala.concurrent.Future[None.type] required Option[SomeModel]。也试过 case _ => None 但没有成功。

def getById(id: String)(implicit ec: ExecutionContext): Future[Option[SomeModel]]={
    this.get( BSONDocument("_id" ->  BSONObjectID(id)) ).map {
      res => Future.successful(res)
    }.recover {
      case _ => Future.successful(None)
    }
  }

def get(query: BSONDocument)(implicit ec: ExecutionContext): Future[Option[SomeModel]]= {
    collection.find(query).one[SomeModel](ReadPreference.Primary)
  }

你在混淆 recover and recoverWith

两个函数都需要 PartialFunction 接受 Throwable 并且两个函数 return 都接受 Future[U],但是

  • recoverPartialFunction应该return一个U
  • recoverWith 应该 return 一个 Future[U].

在你的情况下,你可以使用 recover :

get(BSONDocument("_id" ->  BSONObjectID(id)))
  .recover { case _ => None }
  // you don't need map(res => Future.successful(res)

更新:您可以将 get 编辑为 return 失败的 Future 而不是抛出 IllegalArgumentException。一种可能的方法是使用 Try 及其 recover :

import scala.util.Try

def get(query: BSONDocument)(implicit ec: ExecutionContext): Future[Option[SomeModel]] = 
  Try(collection.find(query).one[SomeModel](ReadPreference.Primary))
    .recover{ case t => Future.failed(t) }.get

更新:

我做的时候它起作用了

def getById(id: String)(implicit ec: ExecutionContext): Future[Option[SomeModel]]={
      Try(this.get(BSONDocument("_id" -> BSONObjectID(id)))).recover{ case t => Future.failed(t) }.get
  }

def get(query: BSONDocument)(implicit ec: ExecutionContext): Future[Option[SomeModel]]={
      collection.find(query).one[SomeModel](ReadPreference.Primary)
  }

据我了解你的问题,

...I don't know how to deal with the IllegalArgumentException thrown when the ID is wrong!

我认为,更好的解决方案是

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)
    }
}

在您的代码中,Scala 在调用 get 方法之前尝试从字符串中解析 BSONObjectId,如果字符串 id 无效,则 BSON 在当前线程中抛出异常(不在 Future 方法的结果中 get).这就是 recover {case _ => Future.successful(None)} 不会执行的原因。方法 recoverrecoverWith 仅在 Future 存储一些异常时执行。例如,此代码也可以工作:

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
    }
}

但是这个变体很复杂(再创建一个 FutureflatMap 它们,用 NonFatal 控制恢复)。我更喜欢使用 parse 方法的第一个变体(没有一些额外的期货和控制,它更容易)。