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

为何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();
        }
}

问题分析与解决思路

  1. 返回码异常原因:
    控制器明确返回HttpStatus.CREATED但实际返回200,大概率是请求未到达create方法。检查Postman请求路径是否为/subreddit/add、请求方法是否为POST、请求头是否携带有效的Authorization Bearer Token。

  2. Subreddit未创建的核心问题:
    控制台无Subreddit插入语句,说明subredditRepository.save()未执行。结合流程排查:

    • 检查AuthenticationService.getCurrentUser()是否抛出异常:若无法获取当前用户(token无效、用户不存在),会中断流程,且未被捕获的异常可能被全局处理器处理,返回默认状态码。
    • 检查mapToSubreddit方法:若subredditDto.getName()为空,插入数据库可能触发约束异常导致事务回滚,需开启DEBUG日志查看详细错误。
    • 事务配置问题:save方法标注@Transactional但未正确提交,可手动调用subredditRepository.flush()验证。
  3. 快速排查步骤:

    • 在SubRedditService.save()方法首尾添加日志,确认方法是否被调用。
    • 验证AuthenticationService.getCurrentUser()实现,确保能从SecurityContextHolder获取当前登录用户。
    • 检查Postman请求JSON体是否包含name和description字段,避免DTO属性为空。
    • 查看Spring Boot错误日志,排查未捕获的异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 05:34:54