You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键改动说明

  1. 后端:

    • 使用@RestController自动返回JSON,无需额外配置
    • 用DayResult类封装每日数据,前端可直接通过属性名获取值,避免字符串分割的错误
    • 移除冗余的类成员变量,保证请求间的独立性
  2. 前端:

    • 用fetch API实现异步提交,避免页面刷新
    • 初始化空图表实例,请求完成后更新数据并调用update()刷新图表
    • 添加多系列数据展示(易感、感染、康复),让模拟结果更直观
    • 给表单添加required属性,确保参数完整

内容的提问来源于stack exchange,提问作者Alex12121

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 13:21:29