Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created November 24, 2025 18:37
Show Gist options
  • Select an option

  • Save sunmeat/6bdb73868880849e46048cc6c1b0a3bb to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/6bdb73868880849e46048cc6c1b0a3bb to your computer and use it in GitHub Desktop.
spring boot + http requests
HibernateApplication.java:
package site.sunmeat.hibernate;
import java.awt.Desktop;
import java.net.URI;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
@SpringBootApplication
public class HibernateApplication {
public static void main(String[] args) {
SpringApplication.run(HibernateApplication.class, args);
}
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder(); // спрінгу буде потрібен білдер для веб-клієнта, без цього не створиться об'єкт і запити не відправляться
}
}
@Component
class BrowserLauncher {
@EventListener(ApplicationReadyEvent.class)
public void launchBrowser() {
System.setProperty("java.awt.headless", "false");
Desktop desktop = Desktop.getDesktop();
try {
desktop.browse(new URI("http://localhost:8080"));
} catch (Exception e) {
}
}
}
===================================================================================================================
WebController.java:
package site.sunmeat.hibernate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.*;
@Controller
public class WebController {
private final WebClient webClient;
public WebController(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build();
}
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/api/text")
@ResponseBody
public Mono<Map<String, String>> getText() {
String url = "http://sunmeat.atwebpages.com/volley/plain.txt";
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.map(content -> Map.of(
"title", "Текстовий файл (plain.txt)",
"content", content
))
.onErrorReturn(Map.of("title", "Помилка", "content", "Не вдалося завантажити текстовий файл"));
}
@GetMapping("/api/image")
@ResponseBody
public Mono<Map<String, String>> getImage() {
String url = "https://raw.githubusercontent.com/sunmeat/gallery/main/MyApplication/cats/cat (1).jpg";
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(byte[].class)
.map(bytes -> Map.of(
"title", "Зображення котика",
"imageBase64", "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(bytes)
))
.onErrorReturn(Map.of("title", "Помилка", "content", "Не вдалося завантажити зображення"));
}
@GetMapping("/api/json-object")
@ResponseBody
public Mono<Map<String, String>> getJsonObject() {
String url = "http://sunmeat.atwebpages.com/volley/alex.json";
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.map(json -> Map.of(
"title", "JSON-об’єкт (alex.json)",
"content", json
))
.onErrorReturn(Map.of("title", "Помилка", "content", "Не вдалося завантажити JSON"));
}
@GetMapping("/api/json-array")
@ResponseBody
public Mono<Map<String, String>> getJsonArray() {
String url = "http://sunmeat.atwebpages.com/volley/array.json";
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.map(json -> Map.of(
"title", "JSON-масив (array.json)",
"content", json
))
.onErrorReturn(Map.of("title", "Помилка", "content", "Не вдалося завантажити масив"));
}
@GetMapping("/api/post")
@ResponseBody
public Mono<Map<String, String>> sendPost() {
String url = "http://sunmeat.atwebpages.com/volley/script.php";
return webClient.post()
.uri(url)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.bodyValue("first=10&second=15")
.retrieve()
.bodyToMono(String.class)
.map(response -> Map.of(
"title", "Відповідь від POST-запиту",
"content", response
))
.onErrorReturn(Map.of("title", "Помилка", "content", "Сервер не відповів"));
}
}
===================================================================================================================
static / style.css:
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Playfair+Display:wght@700;900&display=swap');
:root {
--bg: #0F0A05;
--surface: #3D2B1F;
--coffee: #6F4E37;
--coffee-dark: #5D4037;
--gold: #D4AF37;
--gold-light: #E8C56A;
--cream: #D7CCC8;
--text: #EFEBE9;
--shadow: 0 12px 32px rgba(0,0,0,0.45);
--transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
font-family:'Cormorant Garamond',serif;
background:var(--bg);
color:var(--text);
min-height:100vh;
padding:60px 20px;
line-height:1.8;
background:linear-gradient(to bottom,#0F0A05,#1A120B)
}
h1{
font-family:'Playfair Display',serif;
font-size:clamp(3rem,9vw,6.5rem);
text-align:center;
color:var(--gold);
margin:80px 0 100px;
letter-spacing:2px;
text-shadow:0 4px 12px rgba(0,0,0,0.6);
position:relative
}
h1::after{
content:'';
position:absolute;
left:50%;bottom:-30px;
transform:translateX(-50%);
width:220px;
height:4px;
background:linear-gradient(90deg,transparent,var(--gold),var(--gold-light),var(--gold),transparent);
border-radius:2px;
box-shadow:0 0 12px rgba(212,175,55,0.6)
}
.container{max-width:1360px;margin:0 auto;padding:0 20px}
.buttons{
display:grid;
grid-template-columns:repeat(auto-fit,minmax(340px,1fr));
gap:40px;
margin:80px 0
}
button{
position:relative;
background:var(--coffee);
color:var(--cream);
border:none;
padding:38px 24px;
font-family:inherit;
font-size:1.58em;
font-weight:600;
letter-spacing:2.5px;
text-transform:uppercase;
border-radius:18px;
cursor:pointer;
overflow:hidden;
box-shadow:var(--shadow);
transition:var(--transition);
transform:translateZ(0);
will-change:transform
}
button::before{
content:'';
position:absolute;
inset:0;
background:radial-gradient(circle at var(--x,50%) var(--y,50%),
rgba(212,175,55,0.35) 0%,
rgba(212,175,55,0.15) 40%,
transparent 70%);
opacity:0;
transition:opacity .5s cubic-bezier(0.4,0,0.2,1);
pointer-events:none
}
button:hover::before{
opacity:1
}
button:hover{
transform:translateY(-12px);
box-shadow:0 24px 60px rgba(0,0,0,0.6);
background:var(--coffee-dark)
}
button:active{
transform:translateY(-6px)
}
.result-card{
background:var(--surface);
border-radius:22px;
padding:56px;
margin:60px auto;
max-width:1200px;
box-shadow:var(--shadow);
border:1px solid rgba(212,175,55,0.18);
display:none;
animation:fadeIn .9s ease-out
}
@keyframes fadeIn{
from{opacity:0;transform:translateY(40px)}
to{opacity:1;transform:none}
}
.title{
font-family:'Playfair Display',serif;
font-size:2.9em;
text-align:center;
color:var(--gold);
margin-bottom:44px;
letter-spacing:2px;
position:relative
}
.title::after{
content:'';
position:absolute;
left:50%;bottom:-22px;
transform:translateX(-50%);
width:140px;
height:3px;
background:linear-gradient(90deg,transparent,var(--gold),var(--gold-light),var(--gold),transparent);
border-radius:2px;
box-shadow:0 0 10px rgba(212,175,55,0.5)
}
pre{
background:rgba(15,10,5,0.8);
color:var(--cream);
padding:36px;
border-radius:16px;
font-family:'Courier New',monospace;
font-size:1.12em;
line-height:1.9;
border:1px solid rgba(212,175,55,0.22);
overflow-x:auto;
box-shadow:inset 0 8px 24px rgba(0,0,0,0.5)
}
img{
max-width:100%;
border-radius:16px;
border:6px double var(--gold);
box-shadow:0 20px 50px rgba(0,0,0,0.65);
display:block;
margin:36px auto;
transition:transform .7s ease
}
img:hover{
transform:scale(1.03)
}
@media (max-width:768px){
.buttons{grid-template-columns:1fr;gap:32px}
button{padding:32px 20px;font-size:1.45em}
h1{margin:60px 0 80px}
.result-card{padding:40px}
}
===================================================================================================================
templates / index.html:
<!DOCTYPE html>
<html lang="uk" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>HTTP запити</title>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Playfair+Display:wght@700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" th:href="@{style.css}" href="style.css">
</head>
<body>
<div class="container">
<div class="buttons">
<button onclick="loadData('/api/text')">Завантажити текст</button>
<button onclick="loadData('/api/image')">Показати котика</button>
<button onclick="loadData('/api/json-object')">JSON-об’єкт</button>
<button onclick="loadData('/api/json-array')">JSON-масив</button>
<button onclick="loadData('/api/post')">POST-запит</button>
</div>
<div id="result" class="result-card">
<div class="title" id="resultTitle">Натисни кнопку</div>
<div id="resultContent"></div>
</div>
</div>
<script>
// курсор на кнопці - центр градієнта
document.querySelectorAll('button').forEach(btn => {
btn.addEventListener('mousemove', e => {
const rect = btn.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100 + '%';
const y = ((e.clientY - rect.top) / rect.height) * 100 + '%';
btn.style.setProperty('--x', x);
btn.style.setProperty('--y', y);
});
btn.addEventListener('mouseleave', () => {
btn.style.setProperty('--x', '50%');
btn.style.setProperty('--y', '50%');
});
});
async function loadData(url) {
const titleEl = document.getElementById('resultTitle');
const contentEl = document.getElementById('resultContent');
const card = document.getElementById('result');
titleEl.textContent = 'Зачекайте...';
contentEl.innerHTML = '';
card.style.display = 'block';
try {
const res = await fetch(url);
const data = await res.json();
titleEl.textContent = data.title || 'Результат';
if (data.imageBase64) {
contentEl.innerHTML = `<img src="${data.imageBase64}" alt="Кіт">`;
} else if (data.content) {
contentEl.innerHTML = `<pre>${data.content}</pre>`;
}
} catch (err) {
titleEl.textContent = 'Помилка';
contentEl.innerHTML = `<pre style="color:#A0522D;">${err.message}</pre>`;
}
}
</script>
</body>
</html>
===================================================================================================================
build.gradle:
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.0-SNAPSHOT'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'site.sunmeat'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
repositories {
mavenCentral()
maven { url = 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-webflux' // !!!
}
tasks.named('test') {
useJUnitPlatform()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment