Spring Boot中HTML与Rest Controller的数据交互及图表展示问题
解决流行病模拟系统的数据传输与图表适配问题
核心问题分析
当前存在两个关键问题:
- Spring Boot Controller返回的
List<String>格式不易前端解析,且未正确配置为REST接口返回JSON - HTML表单默认提交会刷新页面,无法异步获取数据更新图表
第一步:改造Spring Boot Controller
1.1 调整Controller注解与返回格式
将@Controller改为@RestController(或保留@Controller并给runSimulation方法加@ResponseBody),确保返回JSON格式数据。同时用自定义类封装每日结果,替代逗号分隔的字符串,降低前端解析成本。
1.2 优化模拟逻辑
移除类级别的参数成员变量,因为每次请求的参数都是独立的,直接用方法参数接收即可。
完整改造后的Controller代码
package com.example.epidemietest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.Random; @SpringBootApplication @RestController // 替换@Controller,自动返回JSON public class EpidemicSimulation { @GetMapping("/home") public String home() { return "index"; } @PostMapping("/simulation") public List<DayResult> runSimulation( @RequestParam int population, @RequestParam double infectionRate, @RequestParam double recoveryRate, @RequestParam int days) { int numInfected = (int) (population * infectionRate); int numRecovered = 0; int numSusceptible = population - numInfected; List<DayResult> result = new ArrayList<>(); for (int day = 1; day <= days; day++) { int numRecoveredToday = (int) (numInfected * recoveryRate); if (day > 7) { numRecovered += numRecoveredToday; numInfected -= numRecoveredToday; } int numSusceptibleToday = numSusceptible - numInfected - numRecovered; Random random = new Random(); int numInfectedToday = 0; for (int i = 0; i < numSusceptibleToday; i++) { if (random.nextDouble() < infectionRate) { numInfectedToday++; } } numInfected += numInfectedToday; numSusceptible -= numInfectedToday; // 用自定义对象封装结果 result.add(new DayResult(day, numSusceptible, numInfected, numRecovered)); } return result; } // 自定义每日结果类,用于JSON序列化 static class DayResult { private int day; private int susceptible; private int infected; private int recovered; public DayResult(int day, int susceptible, int infected, int recovered) { this.day = day; this.susceptible = susceptible; this.infected = infected; this.recovered = recovered; } // Getter方法,Spring会自动序列化这些字段为JSON public int getDay() { return day; } public int getSusceptible() { return susceptible; } public int getInfected() { return infected; } public int getRecovered() { return recovered; } } public static void main(String[] args) { SpringApplication.run(EpidemicSimulation.class, args); } }
第二步:改造前端HTML与JS逻辑
2.1 阻止表单默认提交行为
使用AJAX异步提交参数,避免页面刷新,同时接收后端返回的JSON数据。
2.2 解析数据并更新Chart.js图表
将返回的每日结果解析为图表所需的X轴(天数)和Y轴(易感/感染/康复人数)数据,支持多系列展示更直观。
完整改造后的HTML代码
<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simulation-Epidemie</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap" rel="stylesheet"> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script> <style> body { font-family: 'Open Sans', sans-serif; background-color: gainsboro; padding-left: 100px; padding-right: 100px; text-align: center; } .header-image { width: 100%; } .headline { color: blue; text-align: center; font-weight: bold; } .image-container{ background-color: grey; padding: 5px; } .body-text{ background-color: lightslategrey; width: 1200px; margin: auto; text-align: center; } .simulation{ background-color: lightslategrey; width: 1200px; margin: auto; margin-bottom: 50px; text-align: center; } footer { font-family: 'Open Sans', sans-serif; text-align: center; padding: 3px; background-color: lightblue; color: black; width:100%; } </style> </head> <body> <div class="image-container"> <img class="header-image" src="Banner.png" alt="Banner"> </div> <h1 class="headline"> <br>Simulation einer Epidemie </h1> <div class="body-text"> <br> Die Webseite dient zur Simulation und Einschätzung von Epidemien, bezogen auf einer infektionellen Krankheit wie z.B. Viren. <br> <br> Bitte geben Sie in den folgenden Feldern die entsprechenden Daten für die Simulation ein. <br><br><br> <form id="eingabe"> <label for="population">Größe der Population: </label> <input type="number" id="population" name="population" required> <br> <label for="infectionRate">Infektionsrate durch Virus: </label> <input type="number" id="infectionRate" name="infectionRate" step="0.01" min="0" max="1" required> <br> <label for="recoveryRate">Genesenenschutzrate der Menschen: </label> <input type="number" id="recoveryRate" name="recoveryRate" step="0.01" min="0" max="1" required> <br> <label for="days">Zeitraum in Tagen: </label> <input type="number" id="days" name="days" required> <br> <button type="submit">Submit</button> <br><br><br> </form> </div> <div style="width:800px; margin:0 auto;" class="simulation"> <br><br><br> <canvas id="graph" style="width:100%;max-width:600px"></canvas> <script> // 初始化图表实例(空数据) let chartInstance = new Chart("graph", { type: "line", data: { labels: [], datasets: [ { label: 'Anfällig', fill: false, lineTension: 0, backgroundColor: "rgba(0,128,0,1.0)", borderColor: "rgba(0,128,0,0.1)", data: [] }, { label: 'Infiziert', fill: false, lineTension: 0, backgroundColor: "rgba(255,0,0,1.0)", borderColor: "rgba(255,0,0,0.1)", data: [] }, { label: 'Genesen', fill: false, lineTension: 0, backgroundColor: "rgba(0,0,255,1.0)", borderColor: "rgba(0,0,255,0.1)", data: [] } ] }, options: { title: { display: true, text: 'Ergebniss Epidemie-Simulation:' }, scales: { yAxes: [{ ticks: { min: 0 }, scaleLabel: { display: true, labelString: 'Anzahl Personen' } }], xAxes: [{ scaleLabel: { display: true, labelString: 'Tage' } }] } } }); // 表单提交事件处理 document.getElementById('eingabe').addEventListener('submit', function(e) { e.preventDefault(); // 阻止默认表单提交 // 收集表单参数 const formData = new FormData(this); const params = new URLSearchParams(formData); // 发送AJAX请求 fetch('/simulation', { method: 'POST', body: params }) .then(response => response.json()) .then(data => { // 解析数据为图表所需格式 const days = data.map(item => item.day); const susceptible = data.map(item => item.susceptible); const infected = data.map(item => item.infected); const recovered = data.map(item => item.recovered); // 更新图表数据 chartInstance.data.labels = days; chartInstance.data.datasets[0].data = susceptible; chartInstance.data.datasets[1].data = infected; chartInstance.data.datasets[2].data = recovered; chartInstance.update(); // 刷新图表 }) .catch(error => console.error('Fehler bei der Simulation:', error)); }); </script> <br><br><br> </div> <footer> <p>Entwickler: <br> Projekt: Web-Anwendung</p> </footer> </body> </html>
关键改动说明
后端:
- 使用
@RestController自动返回JSON,无需额外配置 - 用
DayResult类封装每日数据,前端可直接通过属性名获取值,避免字符串分割的错误 - 移除冗余的类成员变量,保证请求间的独立性
- 使用
前端:
- 用
fetchAPI实现异步提交,避免页面刷新 - 初始化空图表实例,请求完成后更新数据并调用
update()刷新图表 - 添加多系列数据展示(易感、感染、康复),让模拟结果更直观
- 给表单添加
required属性,确保参数完整
- 用
内容的提问来源于stack exchange,提问作者Alex12121
相关产品推荐
相关产品推荐

