10 Eylül 2019 Salı

SpringMVC HandlerInterceptorAdapter Sınıfı

Giriş
Şu satırı dahil ederiz
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
Açıklaması şöyle. Yani HandlerInterceptor arayüzünden kalıtmaktan daha iyi
If you would like to provide a custom implementation and only care for a few of their methods (if you do not want to create empty methods that requires overriding), it is better to implement an adapter.
Bu sınıf yerine AsyncHandlerInterceptor kullanılabilir.

Bize gelen (incoming) istekleri kesmek için kullanılır. Gönderdiğimiz (outgoing) cevapları kesmek için ClientHttpRequestInterceptor kullanılır.

Kullanım
Interceptor şöyle eklenir
@Configuration
public class RateLimitConfig extends WebMvcConfigurerAdapter {

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new RequestRateLimiter());
  }
}
afterCompletion metodu - HttpServletRequest + HttpServletResponse + handler + Exception
handler örneğin RestController sınıfıdır
Örnek
Şöyle yaparız.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
  Object handler, Exception exception) throws Exception {
  ...
}
Örnek
Elimizde şöyle bir kod olsun. Bu kodda kendi tanımladığımız @RateLimit anotasyonu kullanılıyor
@RestController
public class BankingController { @PublicAccess @ApiOperation("Get All Credit Card products") @RateLimit(limit = 10, key = ALL_CREDIT_PRODUCTS) @RequestMapping(value = ALL_CREDIT_PRODUCTS, method = RequestMethod.GET,
produces = "application/json") public Response<List<Product>> creditCardProducts() { ... } }
Elimizde şöyle bir interceptor olsun
public class RequestRateLimiter extends HandlerInterceptorAdapter {

  private final ConcurrentHashMap<String,Semaphore> localSemaphoreCache;

  RequestRateLimiter() {
  this.localSemaphoreCache = new ConcurrentHashMap<>();
  }
...
}
Semaphore tryAacquire için şöyle yaparız. Bu kod çağrılan RestController'da tan ım @RateLimit anotasyonunu okur ve semaphore'u belirtilen sayı kadar kilitler
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
  if (!(handler instanceof HandlerMethod)) {
    return true;
  }

  HandlerMethod handlerMethod = (HandlerMethod) handler;

  RateLimit rateLimit = handlerMethod.getMethod().getAnnotation(RateLimit.class);
  if (rateLimit != null) { // target method annotated
    Semaphore localSemaphore = computeIfAbsent(rateLimit.key(), rateLimit.limit());
    if (!localSemaphore.tryAcquire()) {
      throw new RuntimeException("Too many calls");
    }
  }
  return true;
}

private Semaphore computeIfAbsent(String key, int rateLimit) {
  return localSemaphoreCache.computeIfAbsent(key, keyKey -> new Semaphore(rateLimit));
}
Semaphore release için şöyle yaparız
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
  if (handler instanceof HandlerMethod){

    HandlerMethod handlerMethod = (HandlerMethod)handler;
    RateLimit rateLimit = handlerMethod.getMethod().getAnnotation(RateLimit.class);
    if (rateLimit != null) { // target method annotated
      Semaphore localSemaphore = localSemaphoreCache.get(rateLimit.key());
      if (localSemaphore != null) {
        localSemaphore.release(1);
      }
    }
  }
}
postHandle metodu - HttpServletRequest + HttpServletResponse + handler + ModelAndView 

Bu metod genellikle preHandle + postHandle şeklinde kullanılır

preHandle metodu - HttpServletRequest + HttpServletResponse + handler
handler örneğin RestController sınıfıdır

Örnek
Şöyle yaparız
- preHandle() içinde HttpServletRequest nesnesindeki parametreler bende varsa false dönülüyor. Http Status Code ise 301
- postHandle() içinde ise eğer Http Status Code 201 ise bazı parametreler saklanıyor. 
Bu kodda halen arka arkaya iki tane istek gelir ve preHandle() çalışırsa problem olabilir, çünkü tam anlamıyla transaction mantığı yok
public class RequestHandlerInterceptor extends HandlerInterceptorAdapter {
  @Override
  public boolean preHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler) throws IOException, ClassNotFoundException {
    String correlationId = request.getHeader("correlation-id");
    if (correlationId != null) {
      String location = getCachedLocationUri(correlationId);
      if (location != null) {
        response.setStatus(301);
        response.addHeader("correlation-id", correlationId);
        response.addHeader("Location", location);
        response.flushBuffer();
        return false;
      }
    }
  return true;
  }
  @Override
  public void postHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler,
                         ModelAndView modelAndView) throws IOException {
    if (response.getStatus() == 201) {
      String correlationId = request.getHeader("correlation-id");
      String location = response.getHeader("Location");
      putLocationInCache(correlationId, location);
    }
  }
}

Hiç yorum yok:

Yorum Gönder