使用参数查询时Http4s连接被拒绝

时间:2019-07-10 14:11:48

标签: scala http4s http4s-circe

我正在向使用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=

0 个答案:

没有答案