I'm developing an API using Spring Boot 3.1.0 to crawl data, I called this localhost:8080/crawl
. Inside this, I also use RestTemplate
to call an external API called third-party.com/data
.
I want to track API calls both my URL and third-party URL.
Here is my table in Oracle:
create table API_CALLS_TRACKING
(
ID NUMBER(20) not null
constraint API_CALLS_TRACKING_PK
primary key,
CLIENT_USERNAME VARCHAR2(20),
FROM_IP VARCHAR2(30),
API_URI VARCHAR2(100),
REQUEST_METHOD VARCHAR2(10),
QUERY_PARAMS VARCHAR2(200),
PATH_PARAMS VARCHAR2(500),
REQUEST_PAYLOAD VARCHAR2(500),
USER_AGENT VARCHAR2(1000),
STATUS_CODE NUMBER(10),
MESSAGE VARCHAR2(100),
THIRTY_SERVICE_API_URI VARCHAR2(100),
THIRTY_SERVICE_RESPONSE_TIME NUMBER(10),
CREATED_AT TIMESTAMP(6) not null,
MODIFIED_AT TIMESTAMP(6) not null
)
Where THIRTY_SERVICE_API_URI
is 3rd-party URL (ex: third-party.com/data
) and THIRTY_SERVICE_RESPONSE_TIME
is exactly response time of 3rd-party service (I'm using StopWatch
to watch response time like this post).
Here is RestTemplateInterceptor
:
@Component
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(RestTemplateInterceptor.class);
@Autowired
private ApiCallsTrackingRepo apiCallsTrackingRepo;
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
StopWatch stopwatch = StopWatch.createStarted();
ClientHttpResponse response = execution.execute(request, body);
stopwatch.stop();
ApiCallsTracking apiCallsTracking = ApiCallsTracking
.builder()
.requestMethod(request.getMethod().name())
.thirtyServiceAPIURI(request.getURI().toString())
.queryParams(request.getURI().getQuery())
.statusCode(response.getStatusCode().value())
.message(response.getStatusText())
.thirtyResponseTime(stopwatch.getTime(TimeUnit.MILLISECONDS))
.build();
apiCallsTrackingRepo.save(apiCallsTracking);
return response;
}
}
But I can't pass HttpServletRequest to interceptor class.
Here is my business flow:
MyController
<-> call 3rd-API via RestTemplate
<-> 3rd-API
Please help me to get request information from my API in interceptor class or guide me a trick to do this.
I want to get HttpServletRequest
information in interceptor class.
Or another word, I want to track both my API and 3rd-API.
To track the both your API & the 3rd party api using RestTemplate and HttpServletRequest in your interceptor class, you can make use of ThreadLocal variables to store the HttpServletRequest information and retrieve it within the interceptor.
First, you need to create a ThreadLocal variable in a separate class to store the HttpServletRequest:
public class RequestHolder {
private static final ThreadLocal<HttpServletRequest> REQUEST_THREAD_LOCAL = new ThreadLocal<>();
public static void setRequest(HttpServletRequest request) {
REQUEST_THREAD_LOCAL.set(request);
}
public static HttpServletRequest getRequest() {
return REQUEST_THREAD_LOCAL.get();
}
public static void clear() {
REQUEST_THREAD_LOCAL.remove();
}
}
you can update your interceptor class to store the HttpServletRequest in the RequestHolder:-
@Component
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor
{
final static Logger log = LoggerFactory.getLogger(RestTemplateInterceptor.class);
@Autowired
private ApiCallsTrackingRepo apiCallsTrackingRepo;
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpServletRequest httpRequest = RequestHolder.getRequest();
String clientUsername = ""; String fromIp = httpRequest.getRemoteAddr();
String apiUri = httpRequest.getRequestURI();
String requestMethod = httpRequest.getMethod();
String queryParams = httpRequest.getQueryString();
String pathParams = "";
String requestPayload = "";
String userAgent = httpRequest.getHeader("User-Agent");
StopWatch stopwatch = StopWatch.createStarted();
ClientHttpResponse response = execution.execute(request, body);
stopwatch.stop();
ApiCallsTracking apiCallsTracking = ApiCallsTracking
.builder()
.clientUsername(clientUsername)
.fromIp(fromIp)
.apiUri(apiUri)
.requestMethod(requestMethod)
.queryParams(queryParams)
.pathParams(pathParams)
.requestPayload(requestPayload)
.userAgent(userAgent)
.statusCode(response.getStatusCode().value())
.message(response.getStatusText())
.thirtyServiceAPIURI(request.getURI().toString())
.thirtyServiceResponseTime(stopwatch.getTime(TimeUnit.MILLISECONDS))
.build();
apiCallsTrackingRepo.save(apiCallsTracking);
return response;
}
}
To populate the HttpServletRequest in the RequestHolder, you can create an HandlerInterceptor implementation as follows:-
@Component
public class RequestInterceptor implement HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
RequestHolder.setRequest(request);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
RequestHolder.clear();
}
}
Make sure to the register this interceptor in your Spring Boot configuration. You can do this by implementing the WebMvcConfigurer interface and overriding the addInterceptors method:-
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private RequestInterceptor requestInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
}
With this setup, the HttpServletRequest information will be stored in the RequestHolder by the RequestInterceptor, and you can retrieve it in the
RestTemplateInterceptor to track both your API and the third-party API.