Содержание
- Изучаю Scala: Часть 1 Игра змейка
- Изучаю Scala: Часть 2 Todo лист с возможностью загрузки картинок
- Изучаю Scala: Часть 3 Юнит Тесты
- Изучаю Scala: Часть 4 WebSocket
- Изучаю Scala: Часть 5 Http Requests
Ссылки
Тестовый контроллер который будет отвечать на наши запросы:
import cats.effect.{ContextShift, IO}import domain.todos.entities.Todoimport io.circe.generic.auto._import sttp.model.CookieWithMetaimport sttp.tapir.json.circe.jsonBodyimport sttp.tapir.{header, _}class TestController(implicit contextShift: ContextShift[IO]) extends ControllerBase { private val baseTestEndpoint = baseEndpoint .in("test") .tag("Test")//Сюда мы будем делать наш запрос private val postTest = baseTestEndpoint .summary("Тестовый эндпойнт для запроска к самому себе") .description("Возвращает тестовые данные") .post .in(header[String]("test_header")) .in(jsonBody[List[Todo]]) .in(cookies) .out(header[String]("test_header_out")) .out(jsonBody[List[Todo]]) .out(setCookies) .serverLogic(x => withStatus(IO { (x._1 + x._3.map(c => c.name + "" + c.value).fold("")((a, b) => a + " " + b), x._2, List(CookieWithMeta(name = "test", value = "test_value"))) }))//Этот метод будет запускать наш запрос private val runHttpRequestTes = baseTestEndpoint .summary("Запускает тестовый запрос к самому себе") .description("Запускает тестовый запрос к самому себе") .get .out(stringBody) .serverLogic(_ => withStatus(runHttp())) def runHttp(): IO[String] = { ClientExamples.execute().as("Ok") } val endpoints = List( postTest, runHttpRequestTes )}
Собственно сам запрос:
import cats.effect.{ContextShift, IO}import com.typesafe.scalalogging.StrictLoggingimport domain.todos.entities.Todoimport io.circe.generic.auto._import org.http4s.circe.CirceEntityCodec.circeEntityEncoderimport org.http4s.client.blaze._import org.http4s.client.middleware.Loggerimport org.http4s.headers._import org.http4s.{MediaType, Uri, _}import org.log4s._import java.time.Instantimport scala.concurrent.ExecutionContext.globalobject ClientExamples extends StrictLogging { private[this] val logger = getLogger def execute()(implicit contextShift: ContextShift[IO]) = {//Создаем клиент BlazeClientBuilder[IO](global).resource.use { client => logger.warn("Start Request")//Оборачиваем его в мидлвар который будет логгировать запросы и ответы. //Указываем логгировать и боди и хедеры val loggedClient = Logger[IO](true, true)(client)//Парсим адресс и небезопасным методом достаем результат val uri = Uri.fromString("http://localhost:8080/api/v1/test").toOption.get//Создаем запрос. Указываем что это будет POST запрос по адресу что мы сформировали ранее val request: Request[IO] = Request[IO](method = Method.POST, uri = uri)//Указываем что в json теле запроса передавать массив todo .withEntity(List(Todo(1, "Test", 2, Instant.now())))//Указываем заголовки которые будут у запроса. Тут один наш кастомный. .withHeaders(Accept(MediaType.application.json), Header(name = "test_header", value = "test_header_value"))//Указываем что с запросом будут оправляться куки с таким значением .addCookie("test_cookie", "test_cookie_value")//Выполняем запрос loggedClient.run(request).use(r => { logger.warn("End Request")//Логгируем статус (200, 404, 500 и т.д) logger.warn(r.status.toString())//Логгируем ответ logger.warn(r.toString())//Пишем в логи хедеры ответа. Там в том числе есть Set-Cookie logger.warn(r.headers.toString())//bodyText возвращает Stream[IO,String] и мы логгируем данные в нем//Можно десериализовать из этого json ответ сервера. r.bodyText.map(t => logger.warn(t)).compile.drain }) } }}
В результате в логах увидим такой текст:
//Наш запрос 02:54:44.634 [ioapp-compute-7] INFO org.http4s.client.middleware.RequestLogger - HTTP/1.1 POST http://localhost:8080/api/v1/test Headers(Accept: application/json, test_header: test_header_value, Cookie: <REDACTED>) body="[{"id":1,"name":"Test","imageId":2,"created":"2020-12-08T23:54:44.627434500Z"}]"//Наш статус 02:54:44.641 [scala-execution-context-global-62] WARN appServices.ClientExamples - 200 OK//Наш ответ02:54:44.641 [scala-execution-context-global-62] WARN appServices.ClientExamples - Response(status=200, headers=Headers(test_header_out: test_header_value test_cookietest_cookie_value, Set-Cookie: <REDACTED>, Content-Type: application/json, Date: Tue, 08 Dec 2020 23:54:44 GMT, Content-Length: 79))//Хедеры нашего ответа02:54:44.641 [scala-execution-context-global-62] WARN appServices.ClientExamples - Headers(test_header_out: test_header_value test_cookietest_cookie_value, Set-Cookie: test=test_value, Content-Type: application/json, Date: Tue, 08 Dec 2020 23:54:44 GMT, Content-Length: 79)//Тело (json) нашего ответа сервера02:54:44.643 [ioapp-compute-6] WARN appServices.ClientExamples - [{"id":1,"name":"Test","imageId":2,"created":"2020-12-08T23:54:44.627434500Z"}]
Тут был специально показан запрос в максимально общем виде. Показано как установить куки, хедеры, тело запроса. Если нужно данные формы отправить или там файл то есть для этого пару способов. Сами данные методом .withEntity выставляются а вот объект формируется по другому
//Тут можно файл отправить через Part.fileData val data = Multipart(parts = Vector(Part.formData("age","18"):Part[IO]))//Или val data= UrlForm(("age","18"))//И создаем запрос val request: Request[IO] = Request[IO](method = Method.POST, uri = uri) .withEntity(data)