Akka Http调用Bing Search API后如何改写响应并转发?
你的方案可行性分析与修复方案
你的核心思路(调用Bing Search API → 获取响应体 → 解析JSON并添加额外字段 → 返回给客户端)完全可行,只是代码里存在异步流程嵌套混乱、JSON操作不完整、错误处理缺失等细节问题,导致无法正常运行。下面是具体的修复步骤和优化后的代码:
核心问题拆解
- 异步流程嵌套地狱:你把
toStrict、runFold、map多层嵌套,导致类型推断混乱,代码可读性极差,也容易出现异步流处理错误。 - 冗余的流处理:
toStrict已经将Akka HTTP的流式实体转换成完整的StrictEntity,不需要再用runFold手动累加字节流。 - Circe JSON操作不完整:
withFocus返回的是ACursor类型,需要提取最终的Json值,同时要处理修改失败的情况(比如原始JSON不是对象)。 - 错误处理缺失:没有处理Bing API请求失败、JSON解析失败、实体转换超时等异常场景,会导致路由抛出未处理的异常。
- 响应类型不匹配:
complete需要的是符合ToResponseMarshallable的类型,你的代码返回的嵌套Future无法被正确识别。
修复后的完整代码
首先确保引入必要的依赖(以sbt为例):
libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-http" % "10.2.9", "io.circe" %% "circe-core" % "0.14.1", "io.circe" %% "circe-generic" % "0.14.1", "io.circe" %% "circe-parser" % "0.14.1", "io.circe" %% "circe-akka-http" % "0.14.1" // 用于自动将Circe Json转为Akka HTTP响应 )
优化后的路由代码
import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.model.{HttpRequest, RawHeader, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.stream.Materializer import io.circe._ import io.circe.generic.auto._ import io.circe.parser._ import io.circe.syntax._ import io.circe.akka.http._ import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.duration._ import java.net.URLEncoder // 假设q是从路由提取的查询参数,k1是你的Bing API密钥 def bingSearchRoute(q: String, k1: String)(implicit system: ActorSystem, mat: Materializer, ec: ExecutionContext) = { // 准备要添加的额外数据 val extraData = Map("key1" -> "value1", "key2" -> "val2").asJson // 编码查询参数,避免URL非法字符 val encodedQuery = URLEncoder.encode(q, "UTF-8") val bingApiUri = s"https://api.cognitive.microsoft.com/bing/v7.0/search?q=$encodedQuery" // 构建完整的异步处理流程 val modifiedResponseFuture: Future[Json] = Http().singleRequest( HttpRequest(uri = bingApiUri) .withHeaders(RawHeader("Ocp-Apim-Subscription-Key", k1)) ).flatMap { originalResponse => // 先检查Bing API的响应状态是否成功 if (originalResponse.status.isSuccess()) { // 将流式实体转为完整的StrictEntity,超时时间2秒 originalResponse.entity.toStrict(2.seconds).map { strictEntity => // 解析原始JSON响应 parse(strictEntity.data.utf8String) match { case Left(parseError) => // 解析失败时返回错误JSON Json.obj("error" -> Json.fromString(s"Failed to parse Bing response: ${parseError.message}")) case Right(originalJson) => // 向原始JSON对象添加额外字段 originalJson.hcursor .withFocus(_.mapObject(_.add("extraData", extraData))) .top // 获取修改后的Json,若修改失败(如原始不是对象)则返回None .getOrElse(originalJson) // 降级处理:修改失败则返回原始JSON } } } else { // Bing API返回错误状态,抛出异常后续由recover处理 Future.failed(new RuntimeException(s"Bing API returned error: ${originalResponse.status}")) } }.recover { // 处理超时异常 case _: akka.stream.impl.StreamTimeoutException => Json.obj("error" -> Json.fromString("Request to Bing API timed out")) // 处理其他所有异常 case e: Exception => Json.obj("error" -> Json.fromString(s"Unexpected error: ${e.getMessage}")) } // 直接返回Future[Json],circe-akka-http会自动转为HttpResponse complete(modifiedResponseFuture) }
关键优化点说明
- 异步流程扁平化:用链式
flatMap替代多层嵌套,让异步逻辑更清晰,类型推断更准确。 - 简化实体读取:利用
toStrict直接获取完整的响应体字节串,省去冗余的流累加操作。 - Circe JSON操作完善:用
.top.getOrElse(originalJson)处理JSON修改失败的场景,避免崩溃。 - 全链路错误处理:覆盖了API请求失败、JSON解析失败、超时等所有异常场景,返回友好的错误响应。
- 自动响应转换:借助
circe-akka-http的自动marshaller,直接返回Future[Json]即可,无需手动构建HttpResponse。
内容的提问来源于stack exchange,提问作者RB_
相关产品推荐
相关产品推荐

