Ensuring API Idempotency with Spring Boot, Redis, and Token Interceptor
This article explains the concept of idempotency, lists common solutions, and demonstrates a practical implementation using Spring Boot, Redis, and a token‑based mechanism with custom annotations and interceptors, including full code examples, testing steps, and important pitfalls to avoid.
Concept
Idempotency means that multiple identical requests must result in only one operation, such as preventing duplicate order creation, double payment, or repeated form submissions.
Common Solutions
Unique index to avoid dirty data
Token mechanism to block repeated submissions
Pessimistic lock (table or row lock)
Optimistic lock based on version number
Distributed lock using Redis (Jedis, Redisson) or Zookeeper
State machine to control status changes
Implementation Overview
The article adopts the token mechanism with Redis to achieve idempotency.
Implementation Idea
For each request that requires idempotency, generate a unique token, store it in Redis, and include the token in the request header or parameters. The interceptor checks if the token exists in Redis; if it does, the request proceeds and the token is deleted. If the token is missing, the request is rejected with a "please do not repeat the operation" message.
Project Overview
Spring Boot
Redis @ApiIdempotent annotation + interceptor @ControllerAdvice for global exception handling
JMeter for load testing
Code Implementation
// pom.xml dependencies (Redis‑Jedis, Lombok, etc.) // JedisUtil utility class with methods: set, setex, get, del, exists, expire, ttl, close // Custom annotation
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {} // ApiIdempotentInterceptor implements HandlerInterceptor
@Autowired private TokenService tokenService;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) return true;
Method method = ((HandlerMethod) handler).getMethod();
if (method.getAnnotation(ApiIdempotent.class) != null) {
tokenService.checkToken(request);
}
return true;
} // TokenServiceImpl
public ServerResponse createToken() {
String str = RandomUtil.UUID32();
String token = Constant.Redis.TOKEN_PREFIX + str;
jedisUtil.set(token, token, Constant.Redis.EXPIRE_TIME_MINUTE);
return ServerResponse.success(token);
}
public void checkToken(HttpServletRequest request) {
String token = request.getHeader(TOKEN_NAME);
if (StringUtils.isBlank(token)) token = request.getParameter(TOKEN_NAME);
if (StringUtils.isBlank(token)) throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg());
if (!jedisUtil.exists(token)) throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
Long del = jedisUtil.del(token);
if (del <= 0) throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
} // TestApplication (SpringBootApplication) registers CorsFilter and the interceptor bean
@Bean public ApiIdempotentInterceptor apiIdempotentInterceptor() { return new ApiIdempotentInterceptor(); } // TokenController provides an endpoint to obtain a token
@GetMapping public ServerResponse token() { return tokenService.createToken(); } // TestController demonstrates an idempotent API
@ApiIdempotent
@PostMapping("testIdempotence")
public ServerResponse testIdempotence() { return testService.testIdempotence(); }Testing
After obtaining a token, use JMeter to simulate 50 concurrent requests with the token as a header or parameter. Requests without a valid token are rejected.
Important Points (Critical)
When deleting the token, always verify the delete result. Failing to check can cause race conditions where multiple threads think the token still exists, leading to duplicate processing.
Summary
The approach ensures each request is unique by using a Redis‑backed token, simplifying idempotency handling through a custom annotation and interceptor, and can also be implemented with Spring AOP.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
