I implemented a rate limiter with Filter.class. However, we encountered that we should not limit successful requests. So, I needed status code of the response. When I get status code in Filter chain, it always returns 200. That means request not processed. When I trigger chain.doFilter status is set but response is in the committed state means read-only. However, I need to return 429 response for the rate limit responses
I tried OncePerRequestFilter.class, lots of wrappers that I forget. I expect to set response body via response status
I came upon lots of answers. However it was like that.
So I removed Filter usage. I use ResponseBodyAdvice globally.
@ControllerAdvice
public class RequestLimitAdvice implements ResponseBodyAdvice<Object> {
private final LoadingCache<String, Integer> requestCountsPerClient;
private static final List<Integer> statusCodes = Arrays.asList(HttpStatus.BAD_REQUEST.value(), HttpStatus.UNAUTHORIZED.value(),
HttpStatus.FORBIDDEN.value(), HttpStatus.NOT_FOUND.value(), HttpStatus.METHOD_NOT_ALLOWED.value(),
HttpStatus.NOT_ACCEPTABLE.value(), HttpStatus.REQUEST_TIMEOUT.value(), HttpStatus.CONFLICT.value(),
HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.NOT_IMPLEMENTED.value(), HttpStatus.SERVICE_UNAVAILABLE.value(),
HttpStatus.GATEWAY_TIMEOUT.value(), HttpStatus.HTTP_VERSION_NOT_SUPPORTED.value());
public RequestLimitAdvice() {
requestCountsPerClient = Caffeine.newBuilder().
expireAfterWrite(1, TimeUnit.MINUTES).build(key -> 0);
}
@Override
public boolean supports(@NotNull MethodParameter returnType, @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType,
@NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
@NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) {
if (response instanceof ServletServerHttpResponse) {
ServletServerHttpResponse httpServletResponse = (ServletServerHttpResponse) (response);
if (statusCodes.contains(httpServletResponse.getServletResponse().getStatus())) {
String client = getClient(((ServletServerHttpRequest) request).getServletRequest());
if (isMaximumRequestsPerSecondExceeded(client)) {
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return Response.notOk(Translator.toLocale("http.status.too_many_requests"), EErrorCode.TOO_MANY_REQUESTS).getError();
}
}
}
return body;
}
So in ResponseBodyAdvice, thanks to set of response status code and modify enable of response body. I could make what I wanted.
From the answers of