赞
踩
在处理接口幂等性之前我们先说说什么是接口幂等性。
幂等性就是对于同一个接口,如果使用相同的参数,执行多次,得到的结果却是一样的,一般主要处理的是添加、删除、更新这三种类型的接口,查询接口是不需要的,查询并不会影响数据库中的数据。
在发送多个参数相同的同一请求时,为了确保只返回一次结果的效果,这就是幂等性。
解决幂等性的办法有很多种,如Token机制、利用MySQL的唯一索引机制来建立去重表、为要处理的结果设置状态字段、使用Redis的setnx、锁机制等等。
接下来小编将基于Token机制来对接口幂等性问题进行处理。
Token机制:
首先客户端请求服务端,获取一个token,每一次请求都获取一个全新的token,将token存入到redis中,然后将token返回给客户端。
客户端将携带刚刚返回的token去请求一个接口,此时服务端收到请求后将对token进行判断,
a.如果token存在redis中,直接删除token,然后继续处理业务请求。
b.如果token不存在redis中,说明token已过期或者当前业务已经执行过了,那么就不在往下处理业务逻辑。
小编将使用拦截器基于token机制来对幂等性进行处理,SpringBoot以及Redis的配置就不进行演示了,直接上干货!
1.自定义注解
首先,我们需要自定义一个注解,将注解添加到需要进行幂等性处理的接口上即可,将来这个接口将会自动进行幂等性处理。
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface Idemponent {
- }
2.定义Redis
定义RedisService类,判断redis中是否存在token令牌,以及保存token令牌和删除token令牌,
在保存token令牌时设定令牌有效时间为5分钟,删除token时判断当前令牌是否还存在redis中,是否已被删除。
- public class RedisService {
- @Autowired
- StringRedisTemplate redisTemplate;
-
- public boolean hasToken(String token) {
- return redisTemplate.hasKey(token);
- }
-
- public boolean deleteToken(String token) {
- //删除前判断token是否被删除
- if(hasToken(token)){
- return redisTemplate.delete(token);
- }
- return false;
- }
-
- public void saveToken(String s) {
- redisTemplate.opsForValue().set(s,s);
- redisTemplate.expire(s, Duration.ofMinutes(5));
- }
- }
3.定义Token令牌
小编定义了一个TokenService来对token令牌的验证以及获取,定义了令牌校验方法checkToken()和令牌获取方法getToken(),令牌内容是由UUID随机生成的。在checkToken()中,如果令牌存在redis中,则删除该令牌。
- public class TokenService {
-
- @Autowired
- RedisService redisService;
-
- public boolean checkToken(HttpServletRequest request, HttpServletResponse response)
- {
- String token = request.getHeader("token");
- if (token == null || "".equals(token)) {
- //如果请求头为空则从参数中获取
- token = request.getParameter("token");
- if (token == null || "".equals(token)) {
- //说明没有传递令牌
- throw new IdemponentException("令牌不存在,请求失败");
- }
- }
- //判断令牌是否存在redis 中
- boolean result = redisService.hasToken(token);
- if (result) {
- //存在令牌
- return redisService.deleteToken(token);
- }
- //如果redis中不存在该令牌
- return false;
- }
-
- public String getToken() {
- String s = UUID.randomUUID().toString();
- redisService.saveToken(s);
- return s;
- }
- }
4.解析自定义注解
通过拦截器来对自定义的注解进行解析,实现HandlerInterceptor接口,重写preHandle方法即可。
- public class IdemponentInterceptor implements HandlerInterceptor {
- @Autowired
- TokenService tokenService;
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- if(handler instanceof HandlerMethod){
- //说明拦截下来的是接口方法
- HandlerMethod handlerMethod = (HandlerMethod) handler;
- // 获取接口方法上的注解
- Idemponent methodAnnotation = handlerMethod.getMethodAnnotation(Idemponent.class);
- if (methodAnnotation != null) {
- //如果注解不为空,说明接口方法存在
- //检验令牌
- boolean b = tokenService.checkToken(request, response);
- if (!b){
- response.setContentType("text/html;charset=utf-8");
- response.getWriter().write("请求重复");
- }
- return b;
- }
- }
- //说明这个接口不存在,则放行
- return true;
- }
- }
5.配置拦截器
做完以上步骤后,最后配置一下拦截器,拦截所有请求。
- public class WebConfig implements WebMvcConfigurer {
-
- @Autowired
- IdemponentInterceptor interceptor;
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- //拦截所有请求
- registry.addInterceptor(interceptor).addPathPatterns("/**");
- }
- }
这样我们的接口幂等性问题就解决了,只需要将自定义注解加在需要处理幂等性的接口上就好啦
最后总结一下我们都做了哪些事:
1.自定义注解,将注解加在需要幂等性处理的接口上方便进行处理。
2.定义RedisService类,调用方法haskey()判断redis中是否存在token令牌,保存token令牌,调用expire()为token设置有效时长,删除token前判断token是否已被删除。
3.定义Token令牌,从请求头或请求参数中获取token信息,如果没有获取token令牌,则抛出异常,说明token没有发送过来,接着判断token是否存在,如果不存在则不再往下进行逻辑处理如果存在则删除,下一次请求访问时就不用继续往下进行逻辑处理。
4.通过拦截器解析自定义注解,拦截器会拦截请求,我们只需获取添加了自定义注解的接口即可,获取接口之后校验token令牌,如果存在则放行,不存在则说明多次发送参数相同的同一请求(注意:第一次发送请求时会从redis中删除token并放行,之后再次请求redis中将不存在该token)
5.配置拦截器,拦截所有请求。
注意:在发送请求前需要先获取token令牌,获取token令牌接口如下:
- @GetMapping("/token")
- public String getToken(){
- return tokenService.getToken();
- }
以上就是使用Token机制处理接口幂等性问题的方法讲解啦,希望能够帮助到小伙伴们!点点赞哦~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。