Akka HTTP+MongoDB使用Actor查询数据时类型转换异常求助
你碰到的这个ClassCastException核心原因其实很好定位:你的Actor直接把Future[Seq[Employee]]这个异步占位符发送给了请求方,而Route里却试图把这个Future对象强制转换成Seq[EmployeeRequest],类型完全不匹配。
直接调用EmployeeRepo.findAll()时,你是在同一个异步上下文里处理Future的结果,所以没问题;但通过Actor调用时,Actor的!操作符直接把Future实例发了出去,而不是Future最终解析出来的员工列表。Route里的mapTo[Seq[EmployeeRequest]]尝试把这个Future对象(具体类型是scala.concurrent.impl.Promise$Transformation)转成Seq,自然就抛出类型转换异常了。
要修复这个问题,你需要让Actor先等待findAll()返回的Future完成,然后把实际的员工列表发送给sender。Akka提供了非常方便的pipeTo模式来处理这种场景,不需要手动写复杂的onComplete回调。
第一步:修改EmployeeActor代码
把原来直接发送Future的逻辑改成用pipeTo,让Future的结果自动发送给请求的sender:
// 别忘了引入Akka的pipeTo工具包 import akka.pattern.pipe class EmployeeActor extends Actor with ActorLogging { private val employeeService: EmployeeService = new EmployeeService() // 引入Actor上下文的dispatcher,用于处理Future的回调 import context.dispatcher override def receive: Receive = { case Save(emp) => log.info(s"received msg saved with employee :$emp") // 同样用pipeTo处理save的Future,避免重复问题 employeeService.saveEmployeeData(emp).pipeTo(sender()) case SearchAll => log.info("received msg find ALL") // 关键修改:把Future的结果pipe给sender,而不是发Future本身 employeeService.findAll.pipeTo(sender()) case Update(emp, id) => log.info(s"received update msg for id $id and employee $emp") employeeService.update(emp, id).pipeTo(sender()) case Delete(id) => log.info(s"received msg for deleting employee of id $id") employeeService.delete(id).pipeTo(sender()) case _ => log.debug("Unhandled msg") } }
第二步:修正Route中的类型不匹配问题
另外,你Route里写了mapTo[Seq[EmployeeRequest]],但employeeService.findAll()返回的是Future[Seq[Employee]](实体类Employee,不是请求类EmployeeRequest),这里也会有类型不匹配的问题。需要把Employee转换成EmployeeRequest:
// 在EmployeeRoute的get路由里修改处理逻辑 (pathEndOrSingleSlash & get){ complete( (actor ? SearchAll) .mapTo[Seq[Employee]] .map(employees => employees.map(emp => EmployeeRequest(emp.name, emp.dateOfBirth.toString)) ) ) }
Actor之间传递的是具体的消息对象,而Future是一个异步结果的占位符,不是最终数据。直接把Future发给sender,对方拿到的只是还没完成的异步任务,不是你想要的员工列表——这是Akka Actor通信里的常见误区,用pipeTo就能完美解决这类异步结果的传递问题。
内容的提问来源于stack exchange,提问作者Niraj Desai

