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

SpringBoot控制器redirect跳转未执行问题排查求助

登录跳转失效问题排查修复

我开发的副业项目延误多日,目前遇到的问题是:控制器中return redirect:/相关语句始终无法按预期执行,代码逻辑看似正常,但跳转操作未生效。技术栈为MySQL、SpringBoot、JPA、Spring Data JPA、Lombok,请求帮忙排查。


相关代码

登录视图代码

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel = "stylesheet" href ="/css/sample.css">
    <link href="https://getbootstrap.com/docs/5.2/assets/css/docs.css" rel="stylesheet">
    <link href="/css/font.css" rel = "stylesheet">
    <link href="/css/navbar.css" rel = "stylesheet">
    
    <title>loginpage</title>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
    <script type="text/javascript">
      function functionclick() {
        let email =  document.getElementById('exampleDropdownFormEmail1').value;
        let password = document.getElementById('exampleDropdownFormPassword1').value;

        if (email == "" || password == "") {
          alert("회원 정보를 입력하세요");
          history.back();
        } else{
          const url = new URL("/trylogin",location);
          url.searchParams.append('email',email);
          url.searchParams.append('password',password);
          location = url;
        }
      }
    </script>
  </head>
  <body class="p-3 m-0 border-0 bd-example">
    <div class="dropdown-menu">
      <form class="px-4 py-3" method="get" action="login">
        <div class="mb-3">
          <label for="exampleDropdownFormEmail1" class="form-label">이메일을 입력하세요</label>
          <input type="text" class="form-control" id="exampleDropdownFormEmail1" placeholder="email@example.com" name = "email">
        </div>
        <div class="mb-3">
          <label for="exampleDropdownFormPassword1" class="form-label">비밀번호를 입력하세요.</label>
          <input type="password" class="form-control" id="exampleDropdownFormPassword1" placeholder="Password" name = "password">
        </div>
        <div class="mb-3">
          <div class="form-check">
            <input type="checkbox" class="form-check-input" id="dropdownCheck">
            <label class="form-check-label" for="dropdownCheck">
              비밀번호 기억하기
            </label>
          </div>
        </div>
        <button type="submit" class="btn btn-primary" ONCLICK="functionclick()">확인</button>
      </form>
      <div class="dropdown-divider"></div>
      <a class="dropdown-item" href="http://localhost:8080/signup">회원가입 하기</a>
      <a class="dropdown-item" href="http://localhost:8080/forgotpassword">비밀번호 찾기</a>
    </div>
  </body>
</html>

LoginController代码

@Controller
public class LoginController {
    @Autowired
    private UserService userService;

    @GetMapping("/trylogin")
    public String login(LoginDto loginDto) {
        try {
            if(userService.login(loginDto).isPresent()) {
                return "redirect:/loginSuccess";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/loginFailed";
        }
        return "redirect:/loginFailed";
    }

    @GetMapping("/loginSuccess")
    public String loginSuccess() {
        return "login_success";
    }

    @GetMapping("/loginFailed")
    public String loginFailed()  {
        return "login_failed";
    }
}

服务层代码

@Service
@RequiredArgsConstructor
@Getter
public class UserService {
    @Autowired
    private UsersRepository usersRepository;

    @Transactional
    public Optional<Users> login(LoginDto loginDto) throws Exception{
        return usersRepository.findByEmailAndPassword(loginDto.getEmail(), loginDto.getPassword());
    }
}

Users实体代码

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"ssn","phone_number"})})
public class Users {
    @Column(name = "email", length = 40, updatable = false, nullable = false)
    @Id
    private String email;

    @Column(name = "password", length = 20, nullable = false)
    private String password;

    @Column(name = "name", length = 25, nullable = false)
    private String name;

    @Column(name = "phone_number", length = 12, nullable = false)
    private String phoneNumber;

    @Column(name =  "ssn", length = 13, nullable = false, updatable = false)
    private String ssn;

    @Embedded
    private Address address;

    @Column(name = "point")
    private Long point;

    @OneToMany(mappedBy = "postedUser")
    private List<PostInfo> uploadedPost = new ArrayList<>();

    @OneToMany(mappedBy = "userId")
    private List<PointHistory> pointHistories = new ArrayList<>();

    @Embedded
    private Dates dates;

    public Users(UserInfoDto userInfoDto){
        this.setEmail(userInfoDto.getEmail());
        this.setPassword(userInfoDto.getPassword());
        this.setPhoneNumber(userInfoDto.getPhone_number());
        this.setSsn(userInfoDto.getSsn());
        this.setPoint(0L);
        this.setName(userInfoDto.getName());

        Address address = new Address(
                userInfoDto.getCity_name(),
                userInfoDto.getTown_name(),
                userInfoDto.getStreet_name(),
                userInfoDto.getZip_code(),
                userInfoDto.getDetails()
        );
        this.setAddress(address);

        List<PostInfo> postInfoList = new ArrayList<>();
        List<PointHistory> pointHistoryList = new ArrayList<>();
        this.setUploadedPost(postInfoList);
        this.setPointHistories(pointHistoryList);

        Dates dates = new Dates(LocalDateTime.now(), LocalDateTime.now());
        this.setDates(dates);
    }

    public void setEmail(String id) { this.email = id; }
    public void setPassword(String password) { this.password = password; }
    public void setName(String name) { this.name = name; }
    public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
    public void setSsn(String ssn) { this.ssn = ssn; }
    public void setAddress(Address address) { this.address = address; }
    public void setPoint(Long point) { this.point = point; }
    public void setUploadedPost(List<PostInfo> uploadedPost) { this.uploadedPost = uploadedPost; }
    public void setPointHistories(List<PointHistory> pointHistories) { this.pointHistories = pointHistories; }
    public void setDates(Dates dates) { this.dates = dates; }
}

LoginDto代码

@Getter
@Setter
@AllArgsConstructor
public class LoginDto {
    private String email;
    private String password;
}

登录成功页面代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Login Success</title>
  <script type = "text/javascript">
    alert("로그인 성공");
    location.href = "/";
  </script>
</head>
<body>
</body>
</html>

问题排查与修复方案

1. 前端按钮冲突导致重复请求

按钮同时设置了type="submit"和onclick事件,点击时会先执行JS跳转逻辑,随后表单还会提交到action="login",两次请求互相干扰,导致跳转结果不符合预期。

修复:
将按钮类型改为button,避免表单自动提交:

<button type="button" class="btn btn-primary" ONCLICK="functionclick()">확인</button>

或者在functionclick末尾添加return false阻止表单提交:

function functionclick() {
    // 原有逻辑...
    } else{
      const url = new URL("/trylogin",location);
      url.searchParams.append('email',email);
      url.searchParams.append('password',password);
      location = url;
    }
    return false; // 添加这行
}

2. LoginDto缺少无参构造方法

Spring MVC进行参数绑定时,需要目标类有无参构造方法。当前LoginDto仅标注了@AllArgsConstructor,没有无参构造,导致loginDto的email和password始终为null,查询不到用户,最终一直跳转到loginFailed。

修复:
给LoginDto添加无参构造:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor // 添加无参构造
public class LoginDto {
    private String email;
    private String password;
}

3. 服务层不必要的事务注解优化

login方法是查询操作,不需要@Transactional注解(或改为只读事务更合理),虽然不影响跳转,但可以优化代码:

// 去掉@Transactional,或改为@Transactional(readOnly = true)
public Optional<Users> login(LoginDto loginDto) throws Exception{
    return usersRepository.findByEmailAndPassword(loginDto.getEmail(), loginDto.getPassword());
}

4. 登录成功页面跳转逻辑调整

login_success.html中JS自动跳转到/的速度过快,可能导致用户看不到登录成功提示,误以为跳转未生效。可以增加延迟:

alert("로그인 성공");
setTimeout(() => {
    location.href = "/";
}, 1500); // 延迟1.5秒跳转

内容的提问来源于stack exchange,提问作者참직김

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:10:25