For now, I think there are a lot of cases when using the API interface. However, if you call the API too often, server-side processing may be delayed. It means that something bad will happen. This time, in order to limit the number of times to access the API, I wrote this article after careful examination and thought.
You need to have knowledge of Spring Boot and Redis.
As an implementation policy, it is implemented in the form of annotation mainly using Spring Boot and Redis. You can see the details by looking at the comment out.
AccessLimit.java
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author Hyman
* @date 2019/12/25 11:12
*/
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
//Time limit(Seconds)
int seconds();
//Maximum number of accesses
int maxCount();
//Login status
boolean needLogin() default true;
}
ApiLimitInterceptor.java
import com.alibaba.fastjson.JSON;
import com.example.demo.action.AccessLimit;
import com.example.demo.redis.RedisService;
import com.example.demo.result.CodeMsg;
import com.example.demo.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
/**
* @author Hyman
* @date 2019/12/25 11:22
*/
@Component
public class ApiLimitInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//Find out if the request is a HandlerMethod
if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
//Get the annotation above the method and see if there is an AccessLimit annotation
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if(accessLimit == null){
//Do nothing without AccessLimit annotation
return true;
}
//Get the time limit
int seconds = accessLimit.seconds();
//Get the maximum number of accesses
int maxCount = accessLimit.maxCount();
boolean login = accessLimit.needLogin();
String key = request.getRequestURI();
//If login is required, do the following:
if(login){
//I will omit the authentication part here
// key+=""+"userInfo";
}
//Get access count from redis
AccessKey ak = AccessKey.withExpire(seconds);
Integer count = redisService.get(ak,key,Integer.class);
//Create a branch and perform each process
if(count == null){
//When accessing for the first time
redisService.set(ak,key,1);
}else if(count < maxCount){
//Access count + 1
redisService.incr(ak,key);
}else{
//If the maximum number of accesses is exceeded, an error message will be created and returned.
render(response,"The maximum number of accesses will be exceeded within the specified time."); //
return false;
}
}
return true;
}
/**
*If the maximum number of accesses is exceeded, an error message will be created and returned.
*/
private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
String str = JSON.toJSONString(Result.error(cm));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}
}
Register the interceptor class created in 2 above in Spring Boot.
WebConfig.java
import com.example.demo.ExceptionHander.ApiLimitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author Hyman
* @date 2019/11/31 15:58
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private ApiLimitInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
I made a controller and put @AccessLimit above the method. Once that's done, let's test with Postman.
ApiLimitController.java
import com.example.demo.result.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Hyman
* @date 2019/12/25 13:21
*/
@Controller
public class ApiLimitController {
//Time: 5 seconds,Maximum number of accesses:5 ,Login status: Required
@AccessLimit(seconds=5, maxCount=5, needLogin=true)
@RequestMapping("/ApiLimitTest")
@ResponseBody
public Result<String> ApiLimitTest(){
return Result.success("Successful request!");
}
}
Thank you for reading to the end. Please do not hesitate to point out any parts that you think are strange. Thank you.