简体   繁体   English

Springboot @DeleteMapping 响应 404,但响应正文为空

[英]Springboot @DeleteMapping respond 404, but response body is empty

I have problem with @DeleteMapping .我对@DeleteMapping有疑问。

Situation is like below.情况如下。

  1. If I request to /v1/cache/{cacheEntry} with method DELETE ,如果我使用DELETE方法向/v1/cache/{cacheEntry}请求,
    It respond with 404 , but body was empty.它以404响应,但正文为空。 no message, no spring default json 404 response message.没有消息,没有 spring 默认 json 404 响应消息。

  2. If i request to /v1/cache/{cacheEntry} with method POST ,如果我使用POST方法向/v1/cache/{cacheEntry}请求,
    It respond with 405 and body was below.它以405响应,身体在下方。 (This action is correct, not a bug.) (此操作是正确的,不是错误。)

  3. If I change @DeleteMapping to @PostMapping , and request /v1/cache/{cacheEntry} with method POST , It respond success with code 200 .如果我将@DeleteMapping更改为@PostMapping ,并使用方法POST请求/v1/cache/{cacheEntry} ,它会使用代码200响应成功。

{
    "timestamp": 1643348039913,
    "status": 405,
    "error": "Method Not Allowed",
    "message": "",
    "path": "/v1/cache/{cacheEntry}"
}

// Controller // Controller


@Slf4j
@RestController
@RequestMapping("/v1/cache")
@RequiredArgsConstructor
public class CacheController {

    private final CacheService cacheService;

    @PostMapping("/{cacheEntry}")
    public CacheClearResponse clearCacheEntry(@PathVariable("cacheEntry") CacheChannels cacheEntry) {
        try {
            log.info("Cache entry :: " + cacheEntry);
            cacheService.evictCacheEntry(cacheEntry);
            return CacheClearResponse.builder()
                    .result(
                            RequestResult.builder()
                                    .code(9200)
                                    .message("SUCCESS")
                                    .build()
                    )
                    .common(
                            Common.builder().build()
                    )
                    .date(LocalDateTime.now())
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            return CacheClearResponse.builder()
                    .result(
                            RequestResult.builder()
                                    .code(9999)
                                    .message(sw.toString())
                                    .build()
                    )
                    .common(
                            Common.builder().build()
                    )
                    .date(LocalDateTime.now())
                    .build();
        }

    }
}
}

// CacheService // 缓存服务

@Service
@RequiredArgsConstructor
public class CacheService {

    private final CacheManager cacheManager;

    public void evictCacheEntry(CacheChannels cacheEntry) {
        Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
        if (cache != null) {
            cache.clear();
        }
    }

    public void evictCache(CacheChannels cacheEntry, String cacheKey) {
        Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
        if (cache != null) {
            cache.evict(cacheKey);
        }
    }
}

// Enum // 枚举

@Getter
@AllArgsConstructor
public enum CacheChannels {
    CACHE_TEN_MIN(Names.CACHE_TEN_MIN, Duration.ofMinutes(10)),
    CACHE_HALF_HR(Names.CACHE_HALF_HR, Duration.ofMinutes(30)),
    CACHE_ONE_HR(Names.CACHE_ONE_HR, Duration.ofHours(1)),
    CACHE_THREE_HR(Names.CACHE_THREE_HR, Duration.ofHours(3)),
    CACHE_SIX_HR(Names.CACHE_SIX_HR, Duration.ofHours(6)),
    CACHE_ONE_DAY(Names.CACHE_ONE_DAY, Duration.ofDays(1));
    private final String cacheName;
    private final Duration cacheTTL;

    public static CacheChannels from(String value) {
        return Arrays.stream(values())
                .filter(cacheChannel -> cacheChannel.cacheName.equalsIgnoreCase(value))
                .findAny()
                .orElse(null);
    }

    public static class Names {

        public static final String CACHE_TEN_MIN = "cache10Minutes";
        public static final String CACHE_HALF_HR = "cache30Minutes";
        public static final String CACHE_ONE_HR = "cache1Hour";
        public static final String CACHE_THREE_HR = "cache3Hours";
        public static final String CACHE_SIX_HR = "cache6Hours";
        public static final String CACHE_ONE_DAY = "cache1Day";
    }
}

// Converter // 转换器

@Slf4j
public class StringToCacheChannelConverter implements Converter<String, CacheChannels> {
    @Override
    public CacheChannels convert(String source) {
        log.info("Convert Target: " + source);
        return CacheChannels.from(source);
    }
}

// Security Config // 安全配置

@Configuration
@EnableWebSecurity
@Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${spring.security.auth-token-header-name:Authorization}")
    private String apiKeyHeader;

    @Value("${spring.security.secret}")
    private String privateApiKey;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        APIKeyAuthFilter filter = new APIKeyAuthFilter(apiKeyHeader);
        filter.setAuthenticationManager(new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication)
                    throws AuthenticationException {
                String requestedApiKey = (String) authentication.getPrincipal();
                if (!privateApiKey.equals(requestedApiKey)) {
                    throw new BadCredentialsException("The API Key was not found or not the expected value");
                }

                authentication.setAuthenticated(true);
                return authentication;
            }
        });

        http
                .csrf().disable()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(filter)
                .authorizeRequests()
                    .antMatchers("/v1/cache/**")
                        .authenticated();
    }
}

// Filter // 筛选

@Slf4j
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {

    private String apiKeyHeader;

    public APIKeyAuthFilter(String apiKeyHeader) {
        this.apiKeyHeader = apiKeyHeader;
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
        log.info("Check authenticated.");
        return httpServletRequest.getHeader(apiKeyHeader);
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
        return "N/A";
    }

}

// Web Config // Web 配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToCacheChannelConverter());
    }

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new HiddenHttpMethodFilter();
    }
}

This can be expected the controller was loaded, endpoint was mapped.这可以预期 controller 已加载,端点已映射。
I tried change @DeleteMapping to @PostMapping and it was successfully respond against to POST request.我尝试将@DeleteMapping更改为@PostMapping ,它成功响应了POST请求。

What am I missing?我错过了什么?

I found reason why received 404 without any messages.我找到了为什么在没有任何消息的情况下收到 404 的原因。 My tomcat is on remote server.我的 tomcat 在远程服务器上。 It configured with security-constraint and disable DELETE method for all enpoints.它配置了security-constraint并为所有端点禁用DELETE方法。

I just comment out it and It work properly with delete method.我只是将其注释掉,它可以与 delete 方法一起正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM