Skip to content

Spring HandlerInterceptor fire and forget

I am wondering what would be the best way to make an HTTP fire and forget web service call inside a HandlerInterceptor (or maybe there is something more suitable). Let’s say I am handling requests and I want to notify some API for each response resulting with HTTP 5XX (by sending the request and response body of the request). Of course, the notifying should happen in some worker thread (in fire and forget fashion).

With WebFluxes WebClient this should be relatively easy (with .subscribeOn(Schedulers.elastic())). But what if WebClient is not available?

Answer

As you can see, for instance, in this related SO question how to trace requests and responses is not always a simple task.

No matter where, in order to perform the fire and forget behavior you described, you can annotate your method as @Async. By doing so, Spring will asynchronously invoke the annotated method.

Consider for instance the following example using RestTemplate:

@Component
public class ErrorNotificationClient {

  private final RestTemplate restTemplate;

  public ErrorNotificationClient(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }

  // Use HttpServletRequest and HttpServletResponse if available, 
  // exceptions, what you consider appropriate and have the ability
  // to obtain
  @Async
  public void notifyError(
    HttpServletRequest request,
    HttpServletResponse response
  ) {
    // Build your request as form url encoded, json, etc
    final String url = "...";

    final HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    // Serialize the request and response as necessary
    final MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("request", "...");
    map.add("response", "...");

    final HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

    // Invoke the remote call
    final ErrorNotificationResult errorNotificationResult = restTemplate.postForObject(url, request, ErrorNotificationResult.class);

    // Consider trace or log the result, for instance
  }
}

To enable Spring asynchronous processing, you need to define the @EnableAsync annotation, in your main configuration or in a specific one:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfiguration {

}