为何Spring接口返回HTTP 200 OK而非201 Created且未创建Subreddit?
问题:POST接口返回200而非201,Subreddit未创建
使用Postman测试/subreddit/add的POST方法时,接口返回HTTP 200 OK而非预期的201 Created,且数据库中无对应Subreddit记录。控制台仅输出以下Hibernate查询语句:
Hibernate:
select
t1_0.id,
t1_0.expired,
t1_0.revoked,
t1_0.token,
t1_0.token_type,
t1_0.user_id
from
token t1_0
where
t1_0.token=?
Hibernate:
select
u1_0.userid,
u1_0.created,
u1_0.email,
u1_0.enabled,
u1_0.password,
u1_0.user_name
from
_user u1_0
where
u1_0.userid=?
相关代码
控制器类(SubRedditController)
package com.example.redditback.Controller; import com.example.redditback.Dto.SubRedditDto; import com.example.redditback.Service.SubRedditService; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/subreddit") @AllArgsConstructor public class SubRedditController { private final SubRedditService subredditService; @GetMapping("/all") public ResponseEntity<List<SubRedditDto>> getAllSubreddits() { List<SubRedditDto> subRedditDtos=subredditService.getAll(); return new ResponseEntity<>(subRedditDtos,HttpStatus.OK); } @GetMapping("/findbyid/{id}") public ResponseEntity<SubRedditDto> getSubreddit(@PathVariable("id") Long id) { SubRedditDto subRedditDto=subredditService.getSubreddit(id); return new ResponseEntity<>(subRedditDto,HttpStatus.OK); } @PostMapping("/add") public ResponseEntity<SubRedditDto> create(@RequestBody SubRedditDto subRedditDto){ SubRedditDto subRedditDto1=subredditService.save(subRedditDto); return new ResponseEntity<>(subRedditDto1,HttpStatus.CREATED); } }
DTO类(SubRedditDto)
package com.example.redditback.Dto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class SubRedditDto { private Long id; private String name; private String description; private Integer postCount; }
Repository接口(SubRedditRepository)
package com.example.redditback.Repository; import com.example.redditback.Entity.SubReddit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.Optional; @Repository public interface SubRedditRepository extends JpaRepository<SubReddit,Long> { Optional<SubReddit> findByName(String subRedditName); Optional<SubReddit> findById(Long id); }
服务类(SubRedditService)
package com.example.redditback.Service; import com.example.redditback.Authentification.AuthenticationService; import com.example.redditback.Dto.SubRedditDto; import com.example.redditback.Entity.SubReddit; import com.example.redditback.Exeption.SubRedditNotFoundExeption; import com.example.redditback.Repository.SubRedditRepository; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import static java.time.Instant.now; import static java.util.stream.Collectors.toList; @Service @AllArgsConstructor @Component public class SubRedditService { private final SubRedditRepository subredditRepository; private final AuthenticationService authenticationService; @Transactional(readOnly = true) public List<SubRedditDto> getAll() { return subredditRepository.findAll() .stream() .map(this::mapToDto) .collect(toList()); } @Transactional public SubRedditDto save(SubRedditDto subRedditDto) { SubReddit subreddit = subredditRepository.save(mapToSubreddit(subRedditDto)); subRedditDto.setId(subreddit.getId()); return subRedditDto; } @Transactional(readOnly = true) public SubRedditDto getSubreddit(Long id) { SubReddit subreddit = subredditRepository.findById(id) .orElseThrow(() -> new SubRedditNotFoundExeption("Subreddit not found with id -" + id)); return mapToDto(subreddit); } private SubRedditDto mapToDto(SubReddit subreddit) { return SubRedditDto.builder() .name(subreddit.getName()) .description(subreddit.getDescription()) .id(subreddit.getId()) .postCount(subreddit.getPosts().size()) .build(); } private SubReddit mapToSubreddit(SubRedditDto subredditDto) { return SubReddit.builder().name("/r/" + subredditDto.getName()) .description(subredditDto.getDescription()) .user(authenticationService.getCurrentUser()) .createdDate(now()).build(); } }
认证相关代码
JWT认证过滤器(JwtAuthenticationFilter)
package com.example.redditback.Configuration; import com.example.redditback.Token.TokenRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final TokenRepository tokenRepository; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal( @NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain ) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); final String jwt; final String userEmail; if (authHeader == null ||!authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } jwt = authHeader.substring(7); userEmail = jwtService.extractUsername(jwt); if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); var isTokenValid=tokenRepository.findByToken(jwt) .map(t -> !t.getExpired() && !t.getRevoked()) .orElse(false); if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authToken); } } } }
安全配置类(SecurityConfiguration)
package com.example.redditback.Configuration; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfiguration{ private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; private final LogoutHandler logoutHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf() .disable() .authorizeHttpRequests() .requestMatchers("/Auth/**") .permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .cors() .and() .logout() .logoutUrl("/lougout") .addLogoutHandler(logoutHandler) .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext() ); return http.build(); } }
问题分析与解决思路
返回码异常原因:
控制器明确返回HttpStatus.CREATED但实际返回200,大概率是请求未到达create方法。检查Postman请求路径是否为/subreddit/add、请求方法是否为POST、请求头是否携带有效的Authorization Bearer Token。Subreddit未创建的核心问题:
控制台无Subreddit插入语句,说明subredditRepository.save()未执行。结合流程排查:- 检查
AuthenticationService.getCurrentUser()是否抛出异常:若无法获取当前用户(token无效、用户不存在),会中断流程,且未被捕获的异常可能被全局处理器处理,返回默认状态码。 - 检查
mapToSubreddit方法:若subredditDto.getName()为空,插入数据库可能触发约束异常导致事务回滚,需开启DEBUG日志查看详细错误。 - 事务配置问题:
save方法标注@Transactional但未正确提交,可手动调用subredditRepository.flush()验证。
- 检查
快速排查步骤:
- 在
SubRedditService.save()方法首尾添加日志,确认方法是否被调用。 - 验证
AuthenticationService.getCurrentUser()实现,确保能从SecurityContextHolder获取当前登录用户。 - 检查Postman请求JSON体是否包含
name和description字段,避免DTO属性为空。 - 查看Spring Boot错误日志,排查未捕获的异常。
- 在
内容的提问来源于stack exchange,提问作者Aymen Messikh
相关产品推荐
相关产品推荐

