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

Spring中访问users/update/{id}提交POST请求遇405方法不支持异常

问题:提交用户更新表单时出现405 Method Not Allowed错误

访问users/update/{id}(例如id=6)页面并点击“add”按钮提交表单时,返回405状态码,日志显示:

Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]

预期流程

  • 访问users/update/{id}触发updateUser()方法,从Repository获取对应ID的用户;
  • 将该用户转换为UserViewModel;
  • 把UserViewModel作为“user”对象传入HTML模板registration-formUpdate的表单(表单代码:<form class="form-horizontal" method="post" th:href="@{~/registration}" th:object="${user}">);
  • 表单提交时将对象传递至/registration端点,由RegistrationController的registrationUsersAdding()方法自动填充表单;
  • 点击“add”按钮后将更新后的用户保存至数据库。

此前在其他应用及本项目的Events模块中使用相同逻辑均正常,但此处出现异常。

相关代码

Controller类

@RequiredArgsConstructor
@Controller
public class RegistrationController {

    private final UserService userService;
    private final UserRepository userRepository;

    @PostMapping("/registration")
    public String registrationUsersAdding(@ModelAttribute("user") @Valid UserViewModel user2,
                                          BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            if (user2.getId() == null) {
                return "registration-form";
            } else {
                return "registration-formUpdate";
            }
        } else {
            if (user2.getId() == null) {
                userService.add(user2);
            } else {
                userService.update(user2);
            }
        }
        return "redirect:/login";
    }

    @Secured("ROLE_ADMIN")
    @GetMapping("users/update/{id}")//toooooooo
    public String updateUser(@PathVariable(value = "id") Long id, Model model) {
        var user = userService.userToViewModel(userRepository.getOne(id));
        model.addAttribute("user", user);
        return "registration-formUpdate";
    }
}

Service类

@Service
@RequiredArgsConstructor
public class UserService implements AddUpdateGetDeleteUser {

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;

    public void add(UserViewModel userViewModel) {
        List<Role> roles;

        Role role = new Role();
        role.setType(RoleType.valueOf(userViewModel.getRoles()));// pomyslec nad tym
        roleRepository.save(role);
        roles = Collections.singletonList(role);

        User user1 = User.builder()
                .firstName(userViewModel.getFirstName())
                .lastName(userViewModel.getLastName())
                .login(userViewModel.getLogin())
                .password(passwordEncoder.encode(userViewModel.getPassword()))
                .email(userViewModel.getEmail())
                .roles(roles)
                .build();
        userRepository.save(user1);
    }

    public void update(UserViewModel userDto) {
        Optional<User> optionalUser = userRepository.findById(userDto.getId());
        if (optionalUser.isPresent()) {
            User user = optionalUser.get();
            user.setId(userDto.getId());
            user.setFirstName(userDto.getFirstName());
            user.setLastName(userDto.getLastName());
            user.setRoles(Collections.singletonList(GetRole(userDto.getRoles())));
            user.setLogin(userDto.getLogin());
            user.setEmail(userDto.getEmail());
            user.setPassword(passwordEncoder.encode(userDto.getPassword()));
            userRepository.save(user);
        }
    }

    public UserViewModel userToViewModel(User user) {
        return UserViewModel.builder()
                .id(user.getId())
                .firstName(user.getFirstName())
                .lastName(user.getLastName())
                .roles(user.getRoles().get(0).toString())
                .login(user.getLogin())
                .email(user.getEmail())
                .password(passwordEncoder.encode(user.getPassword()))
                .build();
    }

    private Role GetRole(String roleType) {
        var role = new Role();
        role.setType(RoleType.valueOf(roleType));

        return role;
    }
}

日志信息

2022-12-30 00:35:43.807  INFO 5804 --- [           main] Z.Z.ZadanieRekrutacyjneApplication       : Started ZadanieRekrutacyjneApplication in 10.364 seconds (JVM running for 11.253)
2022-12-30 00:36:28.052  INFO 5804 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-12-30 00:36:28.052  INFO 5804 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-12-30 00:36:28.055  INFO 5804 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms
2022-12-30 00:36:28.527  WARN 5804 --- [nio-8080-exec-1] o.a.c.util.SessionIdGeneratorBase        : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [387] milliseconds.
2022-12-30 00:37:13.180  WARN 5804 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
2022-12-30 00:42:59.615  INFO 5804 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2022-12-30 00:42:59.621  INFO 5804 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2022-12-30 00:42:59.624  INFO 5804 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-12-30 00:42:59.646  INFO 5804 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

问题原因及解决方案

核心原因

日志明确提示POST方法不被支持,说明表单提交的目标端点/registration存在路径解析或映射异常,结合代码排查可能的点:

  1. 表单路径写法错误:@{~/registration}中的~符号可能导致Spring无法正确解析路径,无法匹配到@PostMapping("/registration");
  2. 权限拦截:Spring Security可能拦截了/registration的POST请求;
  3. 废弃方法风险:userRepository.getOne(id)已废弃,可能引发懒加载或数据获取异常,导致后续流程出错;
  4. 表单缺少ID字段:提交时未携带用户ID,可能引发逻辑分支错误或参数接收异常。

具体修复步骤

  1. 修正表单路径:将HTML表单中的th:href="@{~/registration}"改为th:href="@{/registration}",确保路径正确映射到Controller的POST端点;
  2. 添加ID隐藏字段:在表单中加入用户ID的隐藏字段,保证后端能正确接收更新标识:
    <input type="hidden" th:field="*{id}" />
    
  3. 替换废弃方法:将userRepository.getOne(id)改为userRepository.findById(id).orElseThrow(...),避免潜在异常:
    var user = userService.userToViewModel(userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("User not found with id: " + id)));
    
  4. 检查Security配置:确保/registration路径允许POST请求访问,在Security配置中添加:
    .authorizeHttpRequests(auth -> auth
        .requestMatchers(HttpMethod.POST, "/registration").permitAll()
        // 其他权限规则...
    )
    
  5. 验证组件扫描:确认RegistrationController被Spring正确扫描,没有被排除在组件扫描范围外。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 22:25:18