Rest API return 500 when exceed limit
fathyelshemy opened this issue · comments
Hi ,
I have spring boot 1.5.x as zuul server and I add ratelimiter library with version 1.7.6 with the following config
zuul:
semaphore.maxSemaphores: 800
ratelimit:
enabled: true
repository: JPA
add-response-headers: true
behind-proxy: true
default-policy-list:
- limit: 5
refresh-interval: 60
type:
- origin
deny-request:
response-status-code: 429
and follow doc and success to get headers for limit and when user exceed limit API return 500 in postman and the following exception in log console
2020-11-01 10:16:00.387 [http-nio-8085-exec-4] WARN o.s.c.n.z.f.post.SendErrorFilter - Error during filtering
com.netflix.zuul.exception.ZuulException: 429
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitExceededException.<init>(RateLimitExceededException.java:13)
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.filters.RateLimitPreFilter.lambda$run$0(RateLimitPreFilter.java:117)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.filters.RateLimitPreFilter.run(RateLimitPreFilter.java:82)
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117)
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193)
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157)
at com.netflix.zuul.FilterProcessor.preRoute(FilterProcessor.java:133)
at com.netflix.zuul.ZuulRunner.preRoute(ZuulRunner.java:105)
at com.netflix.zuul.http.ZuulServlet.preRoute(ZuulServlet.java:125)
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:74)
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:157)
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.orange.combo.gatewayservice.config.httpLoggingFilter.RequestLogFilter.doFilter(RequestLogFilter.java:97)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.cloud.sleuth.instrument.web.TraceFilter.doFilter(TraceFilter.java:166)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at io.micrometer.spring.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:107)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.orange.combo.gatewayservice.config.apiFactoryFilter.HttpRequestCustomFilter.doFilter(HttpRequestCustomFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Hello @fathyelshemy ,thank you for submitting an issue!
Hi @fathyelshemy the line 1.x.x.RELEASE
has reached its End Of Life a year ago. I recommend you to upgrade your application to the latest Spring Boot, Spring Cloud, and Spring Cloud RateLimit.
Hi @marcosbarbero I upgrade my spring boot app to 2.2.7 and still the same error it return 500 from postman put in log throw above exceptions and I debug a lot and Can't find the problem
Hi @fathyelshemy, did you also update this library to the latest version?
@marcosbarbero
yes now my project works on spring 2.2.7 and JDK8 and the issue is still exists and after long invest I thing their is some issues in Filters because it go to filterchain.dofilter in LogFilterService.java and return with correct response in all cases and then disappear with 500 internal server error without any clear exceptions only com.netflix.zuul.exception.ZuulException: 429
so I'll put all my custom filters to try to discover whats the error
@Component
public class RequestLogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger("kibana-logger");
private final String regex;
public RequestLogFilter() {
this.regex = Stream
.of(".*/v2/api-docs", ".*/swagger-resources", ".*/swagger-ui.html", ".*/webjars", ".*/bot-management/getCampaign")
.reduce((str1, str2) -> String.format("%s|%s", str1, str2))
.map(str -> String.format("(%s).*", str))
.orElse("");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final String path = ((HttpServletRequest) request).getServletPath();
if (!path.matches(this.regex)) {
request = new CustomHttpServletRequestWrapper((HttpServletRequest) request);
response = new CustomHttpServletResponseWrapper((HttpServletResponse) response);
if (((HttpServletRequest) request).getHeader("authorization") != null) {
final String rolePrefix = "ROLE_";
final String defaultRoleName = "ROLE_GUEST";
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String email = authentication.getName();
final String roleName = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).filter(authority -> authority.startsWith(rolePrefix)).findFirst().orElse(defaultRoleName)
.replaceFirst(rolePrefix, "");
MDC.put("email", email);
MDC.put("role", roleName);
} else {
MDC.remove("email");
MDC.remove("role");
}
if (((HttpServletRequest) request).getHeader("host") != null) {
final String host = ((HttpServletRequest) request).getHeader("host");
MDC.put("host", host);
}
/* request info */
final String method = ((HttpServletRequest) request).getMethod();
final String url = ((HttpServletRequest) request).getRequestURL().toString();
logger.info(String.format("REST, request, %s, %s", method, url));
/* request headers */
final List<String> requestHeaders = new LinkedList<>();
final Enumeration<String> headerNames = ((HttpServletRequest) request).getHeaderNames();
while (headerNames.hasMoreElements()) {
final String headerName = headerNames.nextElement();
if (!headerName.equals("authorization")) {
final String headerValue = ((HttpServletRequest) request).getHeader(headerName);
requestHeaders.add(String.format("%s: %s", headerName, headerValue));
}
}
logger.info(String.format("REST, headers, %s", requestHeaders.toString()));
/* request data */
final String requestData = ((CustomHttpServletRequestWrapper) request).getBody().replaceAll("\n", "");
final StringBuilder stringBuilder = new StringBuilder();
if (request.getParameterMap().size() > 0) {
stringBuilder.append("{");
request.getParameterMap().forEach((key, value) -> stringBuilder.append(key).append(": ").append(value[0]).append(", "));
stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
stringBuilder.append("}");
}
logger.info(String.format("REST, data, %s, %s", requestData, stringBuilder.toString()));
/* response */
try {
// --problem is hear
chain.doFilter(request, response);
response.flushBuffer();
}finally {
final String removingWhitespacesRegex = "\n";
final Integer status = ((CustomHttpServletResponseWrapper) response).getStatus();
final String contentType = response.getContentType();
String responseData = ((CustomHttpServletResponseWrapper) response).getBody().replaceAll(removingWhitespacesRegex, "");
if (status < 400)
logger.info(String.format("REST, response, %d, %s, %s", status, contentType, responseData));
else
logger.error(String.format("REST, response, %d, %s, %s", status, contentType, responseData));
}
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HttpRequestCustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
CustomHeadersHttpServletRequestWrapper request = new CustomHeadersHttpServletRequestWrapper((HttpServletRequest) servletRequest);
String comboToken = request.getHeader("Combo-Token");
if(Objects.nonNull(comboToken)) {
request.addHeader("Authorization", request.getHeader("Combo-Token"));
}
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
}
@Component
public class UserFilter extends ZuulFilter {
private final UserRepository userRepository;
private final HttpErrorHandlingService errorHandlingService;
@Value("${token.validitySeconds}")
private long validitySeconds;
private static final Logger LOGGER = LoggerFactory.getLogger("kibana-logger");
public UserFilter(UserRepository deletedUserRepository, HttpErrorHandlingService errorHandlingService) {
this.userRepository = deletedUserRepository;
this.errorHandlingService = errorHandlingService;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE; //Executed before the request is routed
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true; //Indicates that run() method should be invoked
}
@Override
@SuppressWarnings("unchecked")
public Object run() {
//A wrapper around the request, shared by all filters and is unique to each request
RequestContext requestContext = RequestContext.getCurrentContext();
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String email = authentication.getName();
final User user = userRepository.findByEmail(email);
try {
//Set Response Body here
if(Objects.nonNull(user)) {
if(user.isDeleted())
buildResponse(requestContext, HttpStatus.FORBIDDEN, OrangeErrorInfo.FORBIDDEN_USER, "User is deleted");
else {
//The JWT details are only available for secured routes (i.e. routes requiring access token)
if(authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
final Map<String, Object> jwtClaims = (Map<String, Object>)((OAuth2AuthenticationDetails)authentication.getDetails())
.getDecodedDetails();
if(isInvalidToken(user.getTokenLastUpdated(), (Long)jwtClaims.get("exp")))
buildResponse(requestContext, HttpStatus.UNAUTHORIZED, OrangeErrorInfo.EXPIRED_CREDENTIALS, "COMBO token expired");
}
}
}
} catch (IOException e) {
LOGGER.error("Error while serializing object to json");
}
return null;
}
/**
* builds a response depending on the request context and client error
* @param requestContext the current request context
* @param httpStatus the response HTTP status code
* @param orangeErrorInfo the custom error code and message (ODI standardized)
* @param errorMessage the custom error description message
* @throws IOException if converting the response body object to JSON has failed (propagated)
*/
private void buildResponse(RequestContext requestContext, HttpStatus httpStatus, OrangeErrorInfo orangeErrorInfo,
String errorMessage) throws IOException {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(httpStatus.value());
requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
requestContext.setResponseBody(errorHandlingService.buildErrorResponseBody(
requestContext.getRequest(), orangeErrorInfo, errorMessage));
}
/**
* computes the creation date of the token using the validity duration defined in different environments
* (creation date = expiration date - validity duration) and checks whether this date is earlier that the token's
* last updated date
*
* @param tokenLastUpdated the date where the token was last modified/updated
* @param expiration the token expiration date in seconds
* @return true if the creation date is earlier than last updated date
*/
private boolean isInvalidToken(Date tokenLastUpdated, Long expiration) {
Instant expirationDateTime = Instant.ofEpochSecond(expiration);
Instant tokenLastUpdatedDateTime = tokenLastUpdated.toInstant();
Duration duration = Duration.ofSeconds(validitySeconds);
return expirationDateTime.minus(duration).isBefore(tokenLastUpdatedDateTime.truncatedTo(ChronoUnit.SECONDS));
}
}
@Controller
public class CustomErrorController extends BasicErrorController {
@Autowired
private HttpErrorHandlingService httpErrorHandlingService;
public CustomErrorController(ErrorAttributes errorAttributes, ServerProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorProperties.getError(), errorViewResolvers);
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Exception ex = ((Exception)request.getAttribute("javax.servlet.error.exception"));
if(Objects.nonNull(ex)) {
if(ex.getCause() instanceof MultipartException)
throw new ComboHttpRequestException("The submitted value for content-type header isn't supported", request,
OrangeErrorInfo.INVALID_HEADER_VALUE);
else if(ex instanceof RequestRejectedException)
throw new ComboHttpRequestException(ex.getMessage(), request);
else if(ex instanceof ZuulException)
if(new RateLimitExceededException().getMessage().contains(ex.getMessage()))
throw new ZuulRateLimitException("The application has made too many calls and has exceeded the rate limit for this service",request,OrangeErrorInfo.TOO_MANY_REQUESTS);
}
return super.error(request);
}
@ExceptionHandler(ComboHttpRequestException.class)
public ResponseEntity<String> handleError(ComboHttpRequestException ex) {
try {
return ResponseEntity.badRequest()
.contentType(MediaType.APPLICATION_JSON)
.body(httpErrorHandlingService.buildErrorResponseBody(String.valueOf(ex.getRequest().getAttribute("javax.servlet.error.request_uri")),
ex.getRequest().getMethod(),
ex.getOrangeErrorInfo(),
ex.getMessage()));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.badRequest().build();
}
@ExceptionHandler(ZuulRateLimitException.class)
public ResponseEntity<String> handleZuulError(ZuulRateLimitException ex){
try {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(httpErrorHandlingService.buildErrorResponseBody(String.valueOf(ex.getRequest().getAttribute("javax.servlet.error.request_uri")),
ex.getRequest().getMethod(),
ex.getOrangeErrorInfo(),
ex.getMessage()));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}
go in those filters and return com.netflix.zuul.exception.ZuulException: 429 TOO_MANY_REQUESTS successfully but return 500 in postman
and all of them is working fine before add spring-cloud-zuul-ratelimit and I follow doc to the letter
please help
what's on line 97 of RequestLogFilter
?
do you mean
// --problem is hear
it I am right this is spelling mistake i mean
// -- problem is here ( it goes in filters and fire uncatched exception)
chain.doFilter(request, response);
it return correct response for logging (429 TOO_MANY_REQUESTS) but for logging only and in postman it return 500
I'm sorry I missed your comment, you shared a lot of code.
I think it will be easier if you can share a project reproducing the problem
here you are full service and I add config yml file inside project called gateway-service.yml
https://github.com/fathyelshemy/zuul-service.git
Thanks.
I'm looking into it and I couldn't understand how you're loading this gateway-service.yml
to the application context, can you point it to me?
Also, the project doesn't run as a standalone, I would need a runnable project to investigate further
sorry I'll update repo to make it as standalone project and notify u once I finished thanks
Hi Marco,
I update the repo with same case to simulate here you are the repo zuul-service
to simulate the issue you have to call http://localhost:5050/welcome
4 time It will return 500 in postman and 429 in console logs
I just debugged your sample application, the problem is in your RequestLogFilter
, as you are using javax.servlet.Filter
instead of a ZuulFilter
you must catch the exception/status code returned by the RateLimitPreFilter (ZuulFilter)
and send an error response.
Something like this:
response.sendError(theStatusCodeReturnedByRateLimitLibrary);
return;
Unless you have a strong reason to use servlet filters I would migrate it to a ZuulFilter
to make the transition seamless
thanks for your advice but I can't catch RateLimitExceededException
in RequestLogFilter
I don't why If you can help I'll be thankful to you
and I already made my last option to migrate to ZuulFilter and I implement what I did and push to the zuul-service
and I can't log body of response in case of error fired from zuulExceptions.
I see, but you want to log every request that gives an exception?
Wouldn't it be better to leave this feature to an access_log instead?
Don't get it, please explain more what should I leave
I need log to every exception throw by zuul-service and I am sure that every exception thrown by any service throw zuul I log if successfully
I would just use the access_log on any proxy/load-balancer to log the request body if you need to add it to the zuul service itself I wouldn't mix servlet filters with zuul filters.
Regardless of your approach, it has nothing to do with this library is trying to solve. As you are adding servlet filters you have to deal with the burden of how ZuulFilters and Servlet Filters works
Thanks a lot for your support I handle logging issues using ZuulFilters.
but I have small question about exceptions that throw com.netflix.zuul.exception.ZuulException: 429 TOO_MANY_REQUESTS
how can prevent this exception only from print its stack trace on console
This library relies on Spring Boot's logging feature if you want to completely disable the logging you can just set the logging level to off
, something like this:
logging.level.com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit=off