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

Spring Boot与Flutter登录请求遇404及连接拒绝问题求助

Spring Boot + Flutter登录接口请求问题排查

问题描述

用Spring Boot和Flutter开发移动端登录页面,Tomcat已正常启动并连接数据库,使用Pixel 4 API 31模拟器测试:

  • 请求http://192.168.2.6:8090/users/login时,点击登录按钮出现I/flutter ( 7730): Error: 404
  • 改用http://localhost:8090/users/login或http://127.0.0.1:8090/users/login时,出现连接拒绝错误

相关代码

App.java

@SpringBootApplication
@EnableJpaRepositories("org.example.repository.*")
@ComponentScan(basePackages = { "org.example.controller.*" })
@EntityScan("org.example.model.*")
public class App
{
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

UserController.java

@RestController
@RequestMapping("/users")

public class UserController {

    private final UserService userService;
    private UserMapper userMapper; // Inject UserMapper here

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/register") // in mobile app doesn't need all users should be registered by corresponding gym
    public ResponseEntity<UserDTO> registerUser(@RequestBody UserDTO userDTO) {
        // Receive and validate userDTO from the client

        // Map userDTO to a User entity
        User userEntity = userService.registerUser(userDTO);

        // Map the resulting User entity to a UserDTO for the response
        UserDTO registeredUserDTO = UserMapper.mapToDTO(userEntity);

        return ResponseEntity.ok(registeredUserDTO);
    }

    @PostMapping("/login")
    public ResponseEntity<UserDTO> loginUser(@RequestBody UserDTO userDTO) throws AuthenticationException {
        // Validate the loginRequest, e.g., check for null values

        // Map userDTO to a User entity
        UserDTO loginUserDTO = userService.loginUser(userDTO);

        return ResponseEntity.ok(loginUserDTO);
    }
}

application.properties

server.port=8090
server.tomcat.threads.max=200
server.connection-timeout=5s
server.max-http-header-size=8KB
server.tomcat.max-swallow-size=2MB
server.tomcat.max-http-post-size=2MB
spring.datasource.url=jdbc:mysql://localhost:3306/testdbgymapp?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update

login_page.dart(Flutter应用)

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

import 'home_page.dart';

class LoginDemo extends StatefulWidget {
  @override
  _LoginDemoState createState() => _LoginDemoState();
}

class _LoginDemoState extends State<LoginDemo> {
  final TextEditingController usernameController = TextEditingController();
  final TextEditingController passwordController = TextEditingController();

  Future<void> loginUser() async {
    final response = await http.post(
      Uri.parse('http://192.168.2.6:8090/users/login'),
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(<String, String>{
        'username': usernameController.text,
        'password': passwordController.text,
      }),
    );

    if (response.statusCode == 200) {
      // Successful login
      Navigator.push(
          context, MaterialPageRoute(builder: (_) => HomePage()));
    } else if (response.statusCode == 400) {
      // Invalid username or password
      print('Invalid username or password');
    } else {
      // Handle other error cases
      print('Error: ${response.statusCode}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text("Login Page"),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 60.0),
              child: Center(
                child: Container(
                    width: 200,
                    height: 150,
                    decoration: BoxDecoration(
                        color: Colors.red,
                        borderRadius: BorderRadius.circular(50.0)),
                    child: Image.asset('asset/images/gymApp-logo.png')),
              ),
            ),
            Padding(
              //padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
              padding: EdgeInsets.symmetric(horizontal: 15),
              child: TextField(
                decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: 'Email',
                    hintText: 'Enter valid email id as abc@gmail.com'),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(
                  left: 15.0, right: 15.0, top: 15, bottom: 0),
              //padding: EdgeInsets.symmetric(horizontal: 15),
              child: TextField(

                obscureText: true,
                decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: 'Password',
                    hintText: 'Enter secure password'),
              ),
            ),
            TextButton(
              onPressed: (){
                //TODO FORGOT PASSWORD SCREEN GOES HERE
              },
              child: Text(
                'Forgot Password',
                style: TextStyle(color: Colors.blue, fontSize: 15),
              ),
            ),
            Container(
              height: 50,
              width: 250,
              decoration: BoxDecoration(
                  color: Colors.blue, borderRadius: BorderRadius.circular(20)),
                        child: TextButton(
                onPressed: () {
                  loginUser();
                },
                child: Text(
                  'Login',
                  style: TextStyle(color: Colors.white, fontSize: 25),
                ),
              ),
            ),
            SizedBox(
              height: 130,
            ),
            Text('New User? Create Account')
          ],
        ),
      ),
    );
  }
}

Tomcat日志

2023-09-14T10:38:53.779+03:00 INFO 288 --- [ restartedMain] org.example.App : Starting App using Java 17.0.8 with PID 288 (C:\Users\giorg\IdeaProjects\gymApp\target\classes started by Λιτσα in C:\Users\giorg\IdeaProjects\gymApp)
2023-09-14T10:38:53.782+03:00 INFO 288 --- [ restartedMain] org.example.App : No active profile set, falling back to 1 default profile: "default"
2023-09-14T10:38:53.864+03:00 INFO 288 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2023-09-14T10:38:53.864+03:00 INFO 288 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2023-09-14T10:38:55.143+03:00 INFO 288 --- [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-09-14T10:38:55.159+03:00 INFO 288 --- [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 8 ms. Found 0 JPA repository interfaces.
2023-09-14T10:38:56.647+03:00 INFO 288 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8090 (http)
2023-09-14T10:38:56.669+03:00 INFO 288 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-09-14T10:38:56.669+03:00 INFO 288 --- [ restartedMain] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2023-09-14T10:38:56.787+03:00 INFO 288 --- [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-09-14T10:38:56.788+03:00 INFO 288 --- [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2923 ms
2023-09-14T10:38:56.818+03:00 INFO 288 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-09-14T10:38:57.185+03:00 INFO 288 --- [ restartedMain] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@2cf75a85
2023-09-14T10:38:57.186+03:00 INFO 288 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-09-14T10:38:57.196+03:00 INFO 288 --- [ restartedMain] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:mysql://localhost:3306/testdbgymapp?serverTimezone=UTC'
2023-09-14T10:38:57.461+03:00 INFO 288 --- [ restartedMain] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-09-14T10:38:57.676+03:00 INFO 288 --- [ restartedMain] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.1.7.Final
2023-09-14T10:38:57.963+03:00 INFO 288 --- [ restartedMain] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-09-14T10:38:58.141+03:00 INFO 288 --- [ restartedMain] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2023-09-14T10:38:58.537+03:00 INFO 288 --- [ restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-09-14T10:38:58.548+03:00 INFO 288 --- [ restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-09-14T10:38:58.761+03:00 WARN 288 --- [ restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-09-14T10:38:59.336+03:00 INFO 288 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2023-09-14T10:38:59.366+03:00 INFO 288 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8090 (http) with context path ''
2023-09-14T10:38:59.375+03:00 INFO 288 --- [ restartedMain] org.example.App : Started App in 6.016 seconds (process running for 6.52)
2023-09-14T10:51:26.685+03:00 WARN 288 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1m59s220ms870µs300ns).
2023-09-14T10:53:55.076+03:00 INFO 288 --- [nio-8090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-09-14T10:53:55.077+03:00 INFO 288 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-09-14T10:53:55.079+03:00 INFO 288 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms

排查与解决步骤

  1. 修复404错误:确保Controller被Spring扫描到

    • 检查UserController的包路径是否为org.example.controller,你的@ComponentScan只指定了这个路径,包名不匹配会导致Spring无法识别该Controller。
    • 简化App.java配置:删除@EnableJpaRepositories、@ComponentScan、@EntityScan,因为@SpringBootApplication默认扫描主类(App.java)所在包及其子包,只要所有组件都在org.example或其子包下,无需额外配置,避免路径写错。
  2. 解决连接拒绝:使用模拟器访问本地服务的正确地址

    • 安卓模拟器中localhost/127.0.0.1指向模拟器自身,要访问电脑上的Spring Boot服务,需用专用映射地址http://10.0.2.2:8090/users/login。
    • 确认电脑防火墙允许8090端口的入站请求,且电脑与模拟器处于同一局域网。
  3. 验证请求参数匹配

    • 检查Flutter代码中传递的username、password字段名,是否与后端UserDTO中的字段完全一致(大小写敏感),字段不匹配会导致参数绑定失败。
    • 在application.properties中添加logging.level.web=DEBUG,开启Web请求日志,查看请求是否到达Controller及参数解析情况。
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:00:58