APIインタフェースブラシ
APIインタフェースブラシ
名前の通り、あるインタフェースの誰かがある時間内にN回しか要求できないようにしたい.プロジェクトでよく見られる問題は、連点ボタンが複数回要求されることです.以前はweb側でフォームが繰り返しコミットされていましたが、tokenで解決できます.上記の方法に加えて、前後端を合わせる方法.すべてバックエンドで制御されます.
げんり
あなたが要求したとき、サーバはredisを通じてあなたの要求回数を記録し、回数が制限を超えるとアクセスしません.redisに保存されているkeyは時効性があり、期限が切れると削除されます.
コード実装:
グリッドを少し高く見せるために、注釈をカスタマイズして実現します.
ブロッキングをカスタマイズし、リクエストする前にリクエスト回数チェックを行います.
ブロッキングは書き終わりましたが、登録を追加しなければなりません.
私のは
制御層テストインタフェース、
使用方法:第1種:直接クラスに注釈 を使用する第2種:注釈 を方法的に用いる.
maxCount最大リクエスト数、second代表時間、単位は秒
デフォルトでは1秒以内で、インタフェースごとに1回しか要求できません.
クラスとメソッドに
コードアドレス
完全なコード、次のアドレス
Giteeアドレス:https://gitee.com/rstyro/spring-boot/tree/master/SpringBoot-limit
Githubアドレス:https://github.com/rstyro/Springboot/tree/master/SpringBoot-limit
名前の通り、あるインタフェースの誰かがある時間内にN回しか要求できないようにしたい.プロジェクトでよく見られる問題は、連点ボタンが複数回要求されることです.以前はweb側でフォームが繰り返しコミットされていましたが、tokenで解決できます.上記の方法に加えて、前後端を合わせる方法.すべてバックエンドで制御されます.
げんり
あなたが要求したとき、サーバはredisを通じてあなたの要求回数を記録し、回数が制限を超えるとアクセスしません.redisに保存されているkeyは時効性があり、期限が切れると削除されます.
コード実装:
グリッドを少し高く見せるために、注釈をカスタマイズして実現します.
@RequestLimit
注記import java.lang.annotation.*;
/**
*
*
* @Target ,ElementType.METHOD ,ElementType.TYPE
* (ElementType) :
* 1.CONSTRUCTOR:
* 2.FIELD:
* 3.LOCAL_VARIABLE:
* 4.METHOD:
* 5.PACKAGE:
* 6.PARAMETER:
* 7.TYPE: 、 ( ) enum
* @Retention Annotation : Annotation , ;
* class ; class Annotation ,
* class ( class , Annotation class )。
* meta-Annotation Annotation “ ” 。
* (RetentionPoicy) :
* 1.SOURCE: ( )
* 2.CLASS: class ( class )
* 3.RUNTIME: ( )
*
* @Inherited
* ,@Inherited 。
* @Inherited annotation class, annotation class 。
*/
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
// second , maxCount
int second() default 1;
int maxCount() default 1;
}
RequestLimitIntercept
遮断器ブロッキングをカスタマイズし、リクエストする前にリクエスト回数チェックを行います.
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.lrshuai.limit.annotation.RequestLimit;
import top.lrshuai.limit.common.ApiResultEnum;
import top.lrshuai.limit.common.Result;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
*
*/
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**
* isAssignableFrom() Class Class ,
* isAssignableFrom()
* instanceof
*/
if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
//HandlerMethod , , ,
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//
RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
// , controller
RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
// ,
RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
if(requestLimit != null){
if(isLimit(request,requestLimit)){
resonseOut(response,Result.error(ApiResultEnum.REQUST_LIMIT));
return false;
}
}
}
return super.preHandle(request, response, handler);
}
//
public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
// redis key , , sessionid key, app , ID 。
String limitKey = request.getServletPath()+request.getSession().getId();
// ,
Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
if(redisCount == null){
//
redisTemplate.opsForValue().set(limitKey,1,requestLimit.second(), TimeUnit.SECONDS);
}else{
if(redisCount.intValue() >= requestLimit.maxCount()){
return true;
}
//
redisTemplate.opsForValue().increment(limitKey);
}
return false;
}
/**
*
* @param response
* @param result
* @throws IOException
*/
private void resonseOut(HttpServletResponse response, Result result) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null ;
String json = JSONObject.toJSON(result).toString();
out = response.getWriter();
out.append(json);
}
}
ブロッキングは書き終わりましたが、登録を追加しなければなりません.
WebMvcConfig
構成クラス私のは
Springboot2.*
なので、WebMvcConfigurer
を実現するだけです.springboot1.*
であれば、WebMvcConfigurerAdapter
から継承し、addInterceptors()
を書き直してカスタムブロックを追加すればいいです.@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private RequestLimitIntercept requestLimitIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info(" ");
registry.addInterceptor(requestLimitIntercept);
}
}
Controller
制御層テストインタフェース、
使用方法:
@RequestLimit(maxCount = 5,second = 1)
@RequestLimit(maxCount = 5,second = 1)
maxCount最大リクエスト数、second代表時間、単位は秒
デフォルトでは1秒以内で、インタフェースごとに1回しか要求できません.
@RestController
@RequestMapping("/index")
@RequestLimit(maxCount = 5,second = 1)
public class IndexController {
/**
* @RequestLimit ,
* @return
*/
@GetMapping("/test1")
@RequestLimit
public Result test(){
//TODO ...
return Result.ok();
}
/**
* @RequestLimit ,
* @return
*/
@GetMapping("/test2")
public Result test2(){
//TODO ...
return Result.ok();
}
}
クラスとメソッドに
@RequestLimit
注記が同時にある場合は、メソッド上のパラメータに準じて、注釈が少し多いようです.コードアドレス
完全なコード、次のアドレス
Giteeアドレス:https://gitee.com/rstyro/spring-boot/tree/master/SpringBoot-limit
Githubアドレス:https://github.com/rstyro/Springboot/tree/master/SpringBoot-limit