【发布时间】:2022-02-03 10:26:30
【问题描述】:
@DeleteMapping 有问题。
情况如下。
-
如果我使用方法
DELETE请求/v1/cache/{cacheEntry},
它以404响应,但正文是空的。没有消息,没有 spring 默认的 json 404 响应消息。 -
如果我使用方法
POST请求/v1/cache/{cacheEntry},
它以405响应,正文在下方。 (此操作是正确的,不是错误。) -
如果我将
@DeleteMapping更改为@PostMapping,并使用方法POST请求/v1/cache/{cacheEntry},它会以代码200响应成功。
{
"timestamp": 1643348039913,
"status": 405,
"error": "Method Not Allowed",
"message": "",
"path": "/v1/cache/{cacheEntry}"
}
// 控制器
@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();
}
}
}
}
// 缓存服务
@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);
}
}
}
// 枚举
@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";
}
}
// 转换器
@Slf4j
public class StringToCacheChannelConverter implements Converter<String, CacheChannels> {
@Override
public CacheChannels convert(String source) {
log.info("Convert Target: " + source);
return CacheChannels.from(source);
}
}
// 安全配置
@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();
}
}
// 过滤器
@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";
}
}
// 网络配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCacheChannelConverter());
}
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
}
这可能是控制器已加载,端点已映射。
我尝试将@DeleteMapping 更改为@PostMapping 并成功响应POST 请求。
我错过了什么?
【问题讨论】:
-
deleteSomthing()在做什么?也许找不到您要删除的内容,这正是预期的行为。能否请您添加相关代码?谢谢! -
@JoãoDias 感谢您的回复和格式化。我更清楚地更新代码。
-
@Junghoon Kong尝试二进制调试。意思是注释一半的代码 - 试试它是否有效。如果不这样做,则评论剩余部分的一半并重复直到它这样做。你可以尝试用catch Throwable + log注册@ControllerAdvice bean,看看是否有一些意外的异常。此处的示例baeldung.com/exception-handling-for-rest-with-spring 如果没有,那么我建议从头开始-仅从@RestController 开始-没有安全性,过滤器。作品?添加代码的其他部分。最后的安全性,这可能是这个问题的根源。
标签: java spring spring-boot http-delete