什么是接口的幂等性,如何实现接口幂等性?,mongodb 实战第二版下载
@ControllerAdvice
public class MyControllerAdvice {
@ResponseBody
@ExceptionHandler(ServiceException.class)
public Response serviceExceptionHandler(ServiceException exception){
Response response=new Response(Integer.valueOf(exception.getCode()),exception.getMsg(),null);
return response;
}
}
3.3 编写创建 Token 和验证 Token 的接口以及实现类
@Service
public interface TokenService {
public Response createToken();
public Response checkToken(HttpServletRequest request);
}
具体实现类,核心的业务逻辑都写在注释中了
@Service
public class TokenServiceImpl implements TokenService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public Response createToken() {
//生成 uuid 当作 token
String token = UUID.randomUUID().toString().replaceAll("-","");
//将生成的 token 存入 redis 中
redisTemplate.opsForValue().set(token,token);
//返回正确的结果信息
Response response=new Response(0,token.toString(),null);
return response;
}
@Override
public Response checkToken(HttpServletRequest request) {
//从请求头中获取 token
String token=request.getHeader("token");
if (StringUtils.isBlank(token)){
//如果请求头 token 为空就从参数中获取
token=request.getParameter("token");
//如果都为空抛出参数异常的错误
if (StringUtils.isBlank(token)){
throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getCode().toString(),ResponseCode.ILLEGAL_ARGUMENT.getMsg());
}
}
//如果 redis 中不包含该 token,说明 token 已经被删除了,抛出请求重复异常
if (!redisTemplate.hasKey(token)){
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());
}
//删除 token
Boolean del=redisTemplate.delete(token);
//如果删除不成功(已经被其他请求删除),抛出请求重复异常
if (!del){
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());
}
return new Response(0,"校验成功",null);
}
}
这是比较重要的一步,通过自定义注解在需要实现接口幂等性的方法上添加此注解,实现 token 验证
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
}
接口拦截器
public class ApiIdempotentInterceptor implements HandlerInterceptor {
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod= (HandlerMethod) handler;
Method method=handlerMethod.getMethod();
ApiIdempotent methodAnnotation=method.getAnnotation(ApiIdempotent.class);
if (methodAnnotation != null){
// 校验通过放行,校验不通过全局异常捕获后输出返回结果
tokenService.checkToken(request);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
配置 webConfig,添加拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(apiIdempotentInterceptor());
}
@Bean
public ApiIdempotentInterceptor apiIdempotentInterceptor() {
return new ApiIdempotentInterceptor();
}
}
配置 redis,使得中文可以正常传输
@Configuration
public class RedisConfig {
//自定义的 redistemplate
@Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
//创建一个 RedisTemplate 对象,为了方便返回 key 为 string,value 为 Object
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//设置 json 序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper=new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance);
//string 的序列化
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
//key 采用 string 的序列化方式
template.setKeySerializer(stringRedisSerializer);
//value 采用 jackson 的序列化方式
template.setValueSerializer(jackson2JsonRedisSerializer);
//hashkey 采用 string 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//hashvalue 采用 jackson 的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
最后是 controller
@RestController
@RequestMapping("/token")
public class TokenController {
@Autowired
private TokenService tokenService;
@GetMapping
public Response token(){
return tokenService.createToken();
}
@PostMapping("checktoken")
public Response checktoken(HttpServletRequest request){
return tokenService.checkToken(request);
}
}
评论