How to send response from an interceptor, and halt further execution? - java

I have an interceptor, and under a certain condition I want to send a string response to the browser and then halt execution completely.
How can I do this?

Override the preHandle method and return false if you want to stop execution.
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
response.getWriter().write("something");
return false;
}

You can do like this which will return a json response to the client
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
response.getWriter().write("{ \"error_description\": \"Invalid Value\"}");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(400);
return false;
}

Related

How can we get and set response body in Spring Boot Filter

I have a Spring MVC application which return ResponseEntity and clientResponse object as response body
#RestController
public class XxxController {
public void ResponseEntity(ClientRequest clientRequest) {
...
return ResponseEntity.ok(clientResponse);
}
}
But how can we get the clientResponse object or set a new Response body in Spring Boot Filter?
#Component
public class MyClassFilter implements Filter {
#Override
public void doFilter( HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Not sure what you mean by get Response in Filter. In a filter the request is yet to be passed to controller, so there is no response yet. You can get the request though. But be careful not to read the request as in that case the request stream will be read in the filter and when it arrives at the controller the entire request stream will be already read. To set the response you can do the following:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
response.resetBuffer();
response.setStatus(HttpStatus.OK);
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
response.getOutputStream().print(new ObjectMapper().writeValueAsString(myData));
response.flushBuffer(); // marks response as committed -- if we don't do this the request will go through normally!
}
you can see here why you have to flush the response. You can also do sendError HttpServletResponse#sendError How to change ContentType
If you don't flush the response, your request will continue down the filter chain (you have to add the chain.doFilter(request, response); of course!).
I am not sure with that but I think you can try this :
HttpServletResponse res = (HttpServletResponse) response;
ContentCachingResponseWrapper ccrw= new ContentCachingResponseWrapper(res);
//old body:
String content=new String(ccrw.getContentAsByteArray(), "utf-8");
//try this
HttpServletResponseWrapper hsrw=new HttpServletResponseWrapper(res);
hsrw.getOutputStream().write(/*new body*/);
//OR this
ServletResponseWrapper responseWrapper = (ServletResponseWrapper)response;
responseWrapper.getResponse().resetBuffer();
responseWrapper.getResponse().getOutputStream().write(/*new body*/);

java.lang.IllegalStateException: Cannot call getInputStream() after getReader() has already been called for the current request

I wrote interceptor:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("[pre-handle] method: {}\tURL: {}", request.getMethod(), request.getRequestURL());
if (HttpMethod.POST.name().equals(request.getMethod())) {
log.info(request.getReader().lines().collect(Collectors.joining()));
}
return true;
}
but when I try to call controller, exception throws:
java.lang.IllegalStateException: Cannot call getInputStream() after
getReader() has already been called for the current request
as I understand, getReader() calling closes InputStream & nothing comes to controller.
How to fix it?
To read request multiple time you will need to cache your request before it is read the 1st time. More info can be read here.
Spring MVC provides the ContentCachingRequestWrapper class.
It is a wrapper around the original HttpServletRequest object.
To use it, we must first create a web filter which wraps the original HttpServletRequest:
#Component
public class CachingRequestBodyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
chain.doFilter(wrappedRequest, servletResponse);
}
After which you can get the byte[] content using the getContentAsByteArray method of ContentCachingRequestWrapper in your interceptor.

Unable to to intercept Spring #Async

I have a public method annotated with #Async. This method is not in the controller. I want to intercept the thread after completion of the thread execution so that I can clear certain ThreadLocals.
I already looked into AsyncHandlerInterceptor but this gets hit when the controller receives the request and not before and after the #Async execution.
public class SampleAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
#Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("afterConcurrentHandlingStarted " + request.getRequestURI());
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle " + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle " + request.getRequestURI());
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion " + request.getRequestURI());
}
}
What I would like is an interceptor like CallableProcessingInterceptor, but for #Async operation.
AsyncHandlerInterceptor is for web requests, #Async is general annotation which allows to delegate method execution to thread pool. No surprise these do not work well together.
#Async allows to provide name of thread pool. You can create your own pool which wraps all submitted tasks into your class so later you will intercept all operation with task.

Spring interceptor not working for partial REST endpoints

I have a Spring Boot application with REST endpoints defined like this
/usermanagement/v1/access/ldap
/usermanagement/v1/access/db
I have created a Spring Interceptor to intercept all incoming request with following pattern
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/usermanagement/v1/**");
}
RequestInterceptor
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
System.out.println("This is Post Handling the request");
}
}
This interceptor works only if client accesses the complete endpoint i.e
/usermanagement/v1/access/ldap
In case a partial endpoint is accessed,
/usermanagement/v1/access
interceptor is not called and a 404 is returned to the client.
Is there a way to change this behavior? The reason I am doing this is because I don't want to expose specific endpoints but common endpoints and make internal calls to services and return result through common endpoints.
You are using the wrong method. Try using afterCompletion instead of postHandle
new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//This is called before handeling any request
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//This is called after successfully handeling a request. It will not be called in case of an exception
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//This will always be called after a request, even in case of an exception
}
}
With a request to a undefined endpoint /usermanagement/v1/access Spring will throw an exception. Therefor it never will enter postHandle.

Servlet filter change response?

I have below servlet filter.
public class MyFilter extends BaseServletRequestFilter {
#Override
protected void afterExecutingFilterChain(final ServletRequest requset, FilterResponseWrapper response) throws ServletException {
//To do
}
#Override
protected void beforeExecutingFilterChain(final ServletRequest requset, final FilterResponseWrapper response) throws ServletException{
//Here request needs to be intercepted
//To do
}
}
I have abover filter. My requirement is i need to intercept the request. I need to check some boolean value in the request. If boolean variable is true then request processing should be continued. If boolean variale is false then request should not continue and i need to send some custom response as below.
public enum CustomStatus {
OK("Ok"),
BAD_REQUEST("BadRequest");
private final String value;
CustomStatus(String v) {
value = v;
}
public String value() {
return value;
}
public static CustomStatus fromValue(String v) {
for (CustomStatus c: CustomStatus.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
If request boolean variable's value is false then i have to set above custom status into response and return without processing the request. How can i do that?
Thanks!
If you create a Filter by extending Filter, you can do:
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
if(your status is ok) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(the error code,
"the error message" );
}
}
Use the Filter interface:
public final class XssFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
//check request...
if (ok) {
chain.doFilter(request, response);
} else {
// do something with the response
}
}
Can't be more specific that that, because you don't say exactly where the boolean value you are checking is (is a parameter, or part of the URL, or a cookie, or a header?), neither do you say exactly what you want done with the response.

Categories

Resources