我正在向使用HTTP4s构建的API服务器上的API端点发送请求。只要我不包含查询参数,一切都可以。当我尝试包含查询参数时,出现此错误:
线程“ main”中的异常java.io.IOException:远程计算机拒绝了网络连接。
我正在构建一个具有Web前端的多层应用程序。最终的目的是对API服务器进行Docker化。 API服务器与后端运行的MySQL的Dockerized实例进行中介通信。当前,所有组件都在我的计算机上本地运行,并且我正致力于使用户能够通过Web浏览器上传Excel文件,并将其内容转换为后端的MySQL数据库。
由于MySQL数据模型,我需要在插入数据库后从数据库中检索实验ID,以便将其传递给每个记录。实验ID是实验记录的外键,其数据保存在一个文件中。在MySQL中创建了一个实验实体,并为其分配了一个自动递增的ID,然后将该实验的记录一次上传到数据库中,并且需要该ID才能链接回其父实验。
这里是将数据发送到API服务器的客户端
import cats.effect.{ContextShift, IO, Resource, Timer}
import io.circe.Json
import models.ExperimentData
import io.circe.generic.auto._
import org.http4s.{Method, Request, Uri}
import org.http4s.client.blaze.BlazeClientBuilder
import scala.concurrent.ExecutionContext.global
import org.http4s.client.Client
import io.circe.syntax._
import org.http4s.EntityDecoder
import io.circe.literal._
import org.http4s._
import org.http4s.circe._
import scala.concurrent.duration.Duration
/**
* This class sends requests to the API server.
*/
object ApiClient {
implicit val cs: ContextShift[IO] = IO.contextShift(global)
implicit val timer: Timer[IO] = IO.timer(global)
case class ExperimentId(ExperimentId: Int)
implicit val expIdDecoder: EntityDecoder[IO, ExperimentId] = jsonOf[IO, ExperimentId]
def createClient: Resource[IO, Client[IO]] = BlazeClientBuilder[IO](global)
.withResponseHeaderTimeout(Duration.Inf)
.withIdleTimeout(Duration.Inf)
.withRequestTimeout(Duration.Inf)
.withMaxWaitQueueLimit(Int.MaxValue)
.withMaxConnectionsPerRequestKey(Function.const(Int.MaxValue))
.resource
def sendGetExperimentIdFromFilenameRequest(filename: String): IO[ExperimentId] = createClient.use(client => {
val uriString = s"http://localhost:8081/experiment/id"
println(s"uriString = $uriString")
val theUri: Uri = Uri(path = uriString, query = Query(("filename", Some(filename))))
println(s"theUri = $theUri")
val contentHeader: Header = Header("Content-type", "application/json")
val acceptHeader: Header = Header("Accept", "application/json")
val req: Request[IO] = Request(method = Method.GET, uri = theUri)
.withHeaders(contentHeader, acceptHeader)
.withEntity(filename.asJson)
client.expect[Json](req).flatMap(x => IO(x.as[ExperimentId].getOrElse(ExperimentId(-1))))
})
}
这是它应该提供的服务:
import clients.MySqlClient
import io.circe.generic.auto._
import cats.effect._
import io.circe.literal._
import org.http4s.circe._
import models.{ExperimentData, Metadata}
import models.RawData.RawDataRecord
import org.http4s.dsl.io._
import org.http4s.{EntityDecoder, HttpRoutes, Response}
import scala.concurrent.ExecutionContext
object MySqlRoutingService {
def apply(mySqlClient: MySqlClient)(implicit ec: ExecutionContext): HttpRoutes[IO] =
new MySqlRoutingService(mySqlClient).serve()
}
final class MySqlRoutingService(mySqlClient: MySqlClient)(implicit ec: ExecutionContext) {
private implicit val cs: ContextShift[IO] = IO.contextShift(ec)
implicit val userDecoder: EntityDecoder[IO, ExperimentData] = jsonOf[IO, ExperimentData]
implicit val recordDecoder: EntityDecoder[IO, RawDataRecord] = jsonOf[IO, RawDataRecord]
implicit val metadataDecoder: EntityDecoder[IO, Metadata] = jsonOf[IO, Metadata]
object FilenameQueryParamMatcher extends QueryParamDecoderMatcher[String]("filename")
def serve(): HttpRoutes[IO] = HttpRoutes.of[IO] {
case req@POST -> Root => req.as[ExperimentData].flatMap(uploadExperimentToDatabase)
case req@POST -> Root / "RawData" / IntVar(expId) =>
req.as[RawDataRecord].flatMap(record => uploadRecordToDatabase(expId, record))
case req@POST -> Root / "CompoundTest" => req.as[Metadata].flatMap(_ => Ok())
case GET -> Root => Ok(json"""{"Message": "Successfully uploaded to database"}""")
case GET -> Root / "id" :? FilenameQueryParamMatcher(filename) => {
println("Hit the end point.")
getExperimentIdFromFilename(filename = filename)
}
}
def getExperimentIdFromFilename(filename: String): IO[Response[IO]] =
mySqlClient.getExperimentIdFromFilename(filename)
.flatMap(expId => Ok(json"""{"ExperimentId" : $expId}"""))
}
当我尝试向http://localhost:8081/experiment/id处的API端点发送请求并为其提供查询参数时(如在sendGetExperimentIdFromFilenameRequest函数中看到的那样),出现上述连接被拒绝错误。如果有帮助,则sendGetExperimentIdFromFilenameRequest中我的printlns的输出为:
uriString = http://localhost:8081/experiment/id theUri = http://localhost:8081/experiment/id?filename=