How can i use multiple interceptors for specific request such as POST, Get and PUT? - spring

I am using spring boot and cloud in the project. For logging, I am using Interceptors. Since I am new to interceptors I am having difficulty using multiple interceptors. Like can I use a specific interceptor for the specific task? For example, when I request a post, the POST interceptor is called, when I use GET the get interceptor is called. and how can I code for multiple interceptors too?
I never tried anything yet for that because I am not getting the logic

You can define all HTTP interceptors that you want, every interceptor should implement the logic of intercept an HTTP request.
#Slf4j
#Component
public class GetRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getMethod().equals(HttpMethod.GET.name())) {
log.info("intercepting GET request {}", request.getRequestURI());
}
return true;
}
}
#Slf4j
#Component
public class PostRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getMethod().equals(HttpMethod.POST.name())) {
log.info("intercepting POST request {}", request.getRequestURI());
}
return true;
}
}
And then you have to register them in spring.
#RequiredArgsConstructor
#Configuration
public class WebConfigurer implements WebMvcConfigurer {
private final GetRequestInterceptor getInterceptor;
private final PostRequestInterceptor postRequestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getInterceptor);
registry.addInterceptor(postRequestInterceptor);
}
}

#earandap
Your comment worked Thanks a lot.
Here is my code:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
log.info("[START] [" + request.getMethod() + "] [ URL is: " + request.getRequestURL().toString()
+ " Body is: {}]");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
long startTime = (long) request.getAttribute("startTime");
request.removeAttribute("startTime");
long endTime = System.currentTimeMillis();
log.info("[END] [" + request.getMethod() + "] [ URL is:" + request.getRequestURL().toString()
+ "] [Execution Time: {} miliseconds]", (endTime - startTime));
}

Related

Get Request/Response Body&Header in Spring AOP

I want to get request/response body and header within my aspect before and after if it's available or how to get those .
I mean i think with before annotation should be work for request,
with after annotation should be work for response. Can be ?
What I've tried so far :
I tried logbook library it's very complicated for me i could'nt figured it out how to work with that.So i gave up.
The actuator can do trick but I am doing extra work like how many times the endpoints called etc.So therefore i can't use actuator.
Also i tried to get request headers like below at least but i think this headers coming same all the time.I couldn't get httpservletresponse like how httpservetrequest does.
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
then
request.getHeader("date") but what about requestbody ?
how to get requestbody ? responsebody ? repsonseheader ?
My aspect file :
#Aspect
#Component
public class AppAspect implements ResponseInfo{
#Before("execution(#(#org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void loggingStartPointRequests(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
}
#After("execution(#(#org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void loggingEndPointRequests(JoinPoint joinPoint) throws IOException {
}
}
My Controller Class:
#RestController
public class MainController {
#GetMapping("/people") //
public ResponseEntity<Poeple> getAllPeople(#RequestParam(name = "page", required = false) Integer page,
#RequestParam(name = "size", required = false) Integer size,
#RequestParam(name = "sortBy", required = false) Boolean sortByNameOrEpCount) {
doSomething();
}
}
I had the same problem and if you have your #Aspect annotated with #Component (or any #Autowired candidate) you can simply get the HttpServletRequest like this:
#Aspect
#Component
public class SomeAspect {
#Autowired
HttpServletRequest request;
#Before("...")
public void beforeAdvice(JoinPoint jp){
/* You will have the current request on the request property */
System.out.println(request.getRequestURL());
}
}
I know this is an old question but I hope it'll be helpful.
I think what you need is to implement the interface HandlerInterceptor, it would help you being able to inspect the request and the response. For example:
public class ApiMonitor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// when the client access to your endpoint
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
// when you finished your process
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// after you already returned an answer to the client
}
}
If you want to operate with the object that you're returning just before you send it to the client, then you need AOP, yes. That's an example of how I do it to modify an object on certain endpoints just before it's parsed to json.
#Component
#Aspect
public class MyCustomAOPInterceptor {
/**
* These poincuts check the execution of a method in any (*)
* class of my.package.controller and that start with
* get/list/find plus any other word (*) . For example
* my.package.controller.UserController.getUserById()
*/
#Pointcut("execution(* my.package.controller.*.get*(..))")
public void petitionsStartWithGet() { }
#Pointcut("execution(* my.package.controller.*.list*(..))")
public void petitionsStartWithList() { }
#Pointcut("execution(* my.package.controller.*.find*(..))")
public void petitionsStartWithFind() { }
#AfterReturning(pointcut = "petitionsStartWithGet() || petitionsStartWithList() || petitionsStartWithFind()", returning = "result")
public void translateEntities(JoinPoint joinPoint, Object result) {
// do your stuff; result is the object that you need
}
}

Logging elapsed time of execution in SpringBoot rest API

It could be a simple solution but I am unable to get it done.
I need to log the overall execution time for my request in SpringBoot Rest API.
Request always enters to MainController always and can exit from two places-
Main Restcontroller same method or
ExceptionHandlerController handler method
I have created one custom annotation and injecting it to both main Controller and ExceptionController methods and getting elapsed time for individual methods.
So here is the problem. I need to add these individual times to calculate the total time which I don't want.
Is there any other way to log this information easily.
Aspect class:
#Aspect
#Component
public class PointsAspect {
private final static Logger logger = LoggerFactory.getLogger(PointsAspect.class);
#Around("#annotation(annotation)")
public Object logMethod(final ProceedingJoinPoint proceedingJoinPoint, final LogAround annotation)
throws Throwable {
final long start = System.currentTimeMillis();
Object obj;
try {
logger.debug("Starting...! Method Name - " +proceedingJoinPoint.getSignature().getName());
obj = proceedingJoinPoint.proceed();
} finally {
logger.debug("Exiting...! Method Name - " +proceedingJoinPoint.getSignature().getName() +"Execution Time in Milliseconds:> "+ String.valueOf(System.currentTimeMillis()-start));
}
return obj;
}
}
Marker Interface:
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface LogAround {
}
And this is how I am injecting it:
**ExceptionHandlerController.java**
#LogAround
#ExceptionHandler(HttpMessageNotReadableException.class)
public GenericFailureResponse missingRequestBodyException(HttpServletResponse response,
HttpServletRequest request, Exception ex) throws IOException {
GenericFailureResponse failureResponse = new GenericFailureResponse();
//TODO: exception logic
return failureResponse;
}
**MainController.java**
#LogAround
public String getTotalPoints(#RequestParam(required=true) long memberNo,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
//TODO : some logic
return "something";
}
You can use a simple filter.
#Component
public class LogTimeFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - startTime;
System.out.println("Request take " + duration + " ms");
}
}

OncePerRequestFilter - handling exceptions annotated with #ResponseStatus

I'm looking for a way to log all my requests and responses in the database (1 record = 1 request + 1 response).
My use case in details:
Log record in database with request URL, params, IP, start date etc.
Update database record (when request finish) and save response,
exceptions, end date etc.
I'm trying to do with custom OncePerRequestFilter and it work's almost OK. But I have problem with handling exceptions annotated with annotation #ResponseStatus. This kind of exceptions (thrown in controllers) I can't catch in my custom doFilter method. Do you know any way to capture these exceptions in filter? Unless I should do this in some other way?
AuditFilter:
#Component
public class AuditFilter extends OncePerRequestFilter {
private Logger logger = Logger.getLogger(AuditFilter.class.getName());
private RequestAuditRepository repository;
AuditFilter(RequestAuditRepository repository) {
this.repository = repository;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain);
}
private void doFilterWrapped(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response, FilterChain filterChain)
throws ServletException, IOException {
RequestAuditLog requestAuditLog = new RequestAuditLog();
String catchedExceptionMsg = null;
try {
beforeRequest(requestAuditLog, request);
filterChain.doFilter(request, response);
}
catch (Exception e) {
// Not called when exception with #ResponStatus annotation throwed
catchedExceptionMsg = e.getMessage();
throw e;
}
finally {
afterRequest(requestAuditLog, catchedExceptionMsg, request, response);
response.copyBodyToResponse();
}
}
...
}
BadRequestException:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
I think the BadRequestException is handled even before your custom filter gets triggered and therefore you can't catch this exception in your filter.
What you could do is that you write your own ExceptionHandler additionally to your filter and log your stuff there.
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(BadRequestException.class)
public void handleError(BadRequestException ex) {
// do your stuff here
}
}

How to get httprequest and httpresponse in ApplicationListener in Spring Boot application?

I would like to set browser cookie value after login successfully.
I am able to detect success login event with following code.
#Component
#Slf4j
public class LoginEventListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
log.info("hahaha: " + event.toString());
}
}
Try autowiring HttpServletRequest object into your LoginEventListener component, given that your component in running in a servlet container.
#Component
#Slf4j
public class LoginEventListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Autowired
private HttpServletRequest request;
#Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
log.info("hahaha: " + event.toString());
log.info("Request Object: " + request); // You now have access to the HTTP request object.
}
}
Tks, just share another way to get it.
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.
currentRequestAttributes()).
getRequest();

Make simple servlet filter work with #ControllerAdvice

I've a simple filter just to check if a request contains a special header with static key - no user auth - just to protect endpoints. The idea is to throw an AccessForbiddenException if the key does not match which then will be mapped to response with a class annotated with #ControllerAdvice. However I can't make it work. My #ExceptionHandler isn't called.
ClientKeyFilter
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Controller
import javax.servlet.*
import javax.servlet.http.HttpServletRequest
#Controller //I know that #Component might be here
public class ClientKeyFilter implements Filter {
#Value('${CLIENT_KEY}')
String clientKey
public void init(FilterConfig filterConfig) {}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
req = (HttpServletRequest) req
def reqClientKey = req.getHeader('Client-Key')
if (!clientKey.equals(reqClientKey)) {
throw new AccessForbiddenException('Invalid API key')
}
chain.doFilter(req, res)
}
public void destroy() {}
}
AccessForbiddenException
public class AccessForbiddenException extends RuntimeException {
AccessForbiddenException(String message) {
super(message)
}
}
ExceptionController
#ControllerAdvice
class ExceptionController {
static final Logger logger = LoggerFactory.getLogger(ExceptionController)
#ExceptionHandler(AccessForbiddenException)
public ResponseEntity handleException(HttpServletRequest request, AccessForbiddenException e) {
logger.error('Caught exception.', e)
return new ResponseEntity<>(e.getMessage(), I_AM_A_TEAPOT)
}
}
Where I'm wrong? Can simple servlet filter work with spring-boot's exception mapping?
As specified by the java servlet specification Filters execute always before a Servlet is invoked. Now a #ControllerAdvice is only useful for controller which are executed inside the DispatcherServlet. So using a Filter and expecting a #ControllerAdvice or in this case the #ExceptionHandler, to be invoked isn't going to happen.
You need to either put the same logic in the filter (for writing a JSON response) or instead of a filter use a HandlerInterceptor which does this check. The easiest way is to extend the HandlerInterceptorAdapter and just override and implement the preHandle method and put the logic from the filter into that method.
public class ClientKeyInterceptor extends HandlerInterceptorAdapter {
#Value('${CLIENT_KEY}')
String clientKey
#Override
public boolean preHandle(ServletRequest req, ServletResponse res, Object handler) {
String reqClientKey = req.getHeader('Client-Key')
if (!clientKey.equals(reqClientKey)) {
throw new AccessForbiddenException('Invalid API key')
}
return true;
}
}
You can't use #ControllerAdvice, because it gets called in case of an exception in some controller, but your ClientKeyFilter is not a #Controller.
You should replace the #Controller annotation with the #Component and just set response body and status like this:
#Component
public class ClientKeyFilter implements Filter {
#Value('${CLIENT_KEY}')
String clientKey
public void init(FilterConfig filterConfig) {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String reqClientKey = request.getHeader("Client-Key");
if (!clientKey.equals(reqClientKey)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid API key");
return;
}
chain.doFilter(req, res);
}
public void destroy() {
}
}
Servlet Filters in Java classes are used for the following purposes:
To check requests from client before they access resources at backend.
To check responses from server before sent back to the client.
Exception throw from Filter may not be catch by #ControllerAdvice because in may not reach DispatcherServlet. I am handling in my project as below:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
String token = null;
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && (bearerToken.contains("Bearer "))) {
if (bearerToken.startsWith("Bearer "))
token = bearerToken.substring(7, bearerToken.length());
try {
AuthenticationInfo authInfo = TokenHandler.validateToken(token);
logger.debug("Found id:{}", authInfo.getId());
authInfo.uri = request.getRequestURI();
AuthPersistenceBean persistentBean = new AuthPersistenceBean(authInfo);
SecurityContextHolder.getContext().setAuthentication(persistentBean);
logger.debug("Found id:'{}', added into SecurityContextHolder", authInfo.getId());
} catch (AuthenticationException authException) {
logger.error("User Unauthorized: Invalid token provided");
raiseException(request, response);
return;
} catch (Exception e) {
raiseException(request, response);
return;
}
// Wrapping the error response
private void raiseException(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED);
apiError.setMessage("User Unauthorized: Invalid token provided");
apiError.setPath(request.getRequestURI());
byte[] body = new ObjectMapper().writeValueAsBytes(apiError);
response.getOutputStream().write(body);
}
// ApiError class
public class ApiError {
// 4xx and 5xx
private HttpStatus status;
// holds a user-friendly message about the error.
private String message;
// holds a system message describing the error in more detail.
private String debugMessage;
// returns the part of this request's URL
private String path;
public ApiError(HttpStatus status) {
this();
this.status = status;
}
//setter and getters

Resources