request.getServletPath() returned null from Spring MVC - spring

I made a filter to capture HttpServletRequest sevlet path from all requests
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
// debug to see the output
String path = request.getServletPath();
filterChain.doFilter(request, response);
}
There is a URL in jsp that has no controller or view mapped to it
<div>
<spring:url value="/app" var="app_url" htmlEscape="true"/>
<spring:message code="label_3rd_app" />
</div>
However, when click on the url while debugging on the filter, I see request.getServletPath() value from two request:
/null
/null/app
My question is why request.getServletPath() never returns /app instead?

You're getting null because
request.getServletPath();
is for Servlets, and you're doing it inside a Filter. To get it in a Filter you have to manually build it like this:
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getRequestURI().substring(request.getContextPath().length());
more info why:
How to get request URI without context path?

Mr. Lalibertes solution is not working correctly as the result of getRequestURI is uri-encoded and getServletPath is not.
Better use Springs UrlPathHelper for getting the servlet path in such situations.
new org.springframework.web.util.UrlPathHelper().getPathWithinApplication(servletRequest);
This method delivers the uri-decoded servlet path as getServletPath would have - if it would'nt be null.

For people coming to this question just because your context path was null (like me): I was calling request.getContextPath() in a background thread.
The request then became 'out of scope', if I understand this bug report discussion correctly: https://bugs.eclipse.org/bugs/show_bug.cgi?id=401768
Also, I know I'm not supposed to use normal threads in servlet environments, but use Executor instead. But the problem would probably remain: https://developer.jboss.org/thread/232135.
Solution was to extract all needed info from the request before handing off to background thread.

Related

POST request not getting forwarded from Filter to Controller class

From postman I am hitting a post request like http://localhost:8084/abc/api/v1/xyz having payload and header. we have configured a Filter class extending GenericFilterBean before it hits the Controller. Its executing all the codes of Filter class fine but while executing 'chain.doFilter(request, response);' in the end to forward request to controller method its throwing below exception.In Filter class we are reading the payload and saving in audit table. In Controller class method we have parameters like #RequestBody annotaion, #Context HttpServletRequest, BindingResult.
18:59:25,779 INFO [stdout] (default task-1) 18:59:25,778||WARN |AbstractHandlerExceptionResolver:197|Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: UT010029: Stream is closed]
Kindly suggest on this.
Is your filter reading the contents of the request? If so then you'll need to look at alternatives to this, as the input stream is not likely to be reusable without some assistance.
Can you post some code from the filter method?
Spring provides at least one mechanism to work around this, which might be helpful here, specifically the ContentCachingRequestWrapper. You can either create a new Filter which wraps the actual request in this wrapper, or you can simply make use of it yourself in your filter.
You might update your filter with something like this
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
// todo: your magic code, using wrappedRequest to access the body of the request
// note: passing through the wrapped request,
// which allows later handlers to also access the request
chain.doFilter(wrappedRequest, servletResponse);
}
Note the documentation for ContentCachingRequestWrapper notes
HttpServletRequest wrapper that caches all content read from the input stream and reader, and allows this content to be retrieved via a byte array.
The error that you're receiving indicates you're reading the InputStream of the request, and you should rather simply access the getContentAsByteArray method of the wrapper.

ContentCachingRequestWrapper getContentAsByteArray() method return only 8000 bytes

I`m using ContentCachingRequestWrapper to cache my request in Spring Boot filter. Unfortunatelly, when I use method getContentAsByteArray() to get content of my request - I get only array with size 8000 bytes.
I haven`t got any post limit in Tomcat. What is more, when I check size of request earlier - it is correct.
Do you know why ContentCachingRequestWrapper.getContentAsByteArray() return only 8000 bytes?
Code:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
chain.doFilter(requestWrapper, response);
//here --> requestWrapper.getContentAsByteArray().length = 8000 (for larger request)
savemyRequest(new String(requestWrapper.getContentAsByteArray()), response.getStatus());
}
I encountered a similar issue and decided to keep what I got here.
People in the comments already mentioned a bit.
ContentCachingRequestWrapper itself does not read the request body. It wraps the request body with (as class name implies) PushBackInputStream. As soon as the request body is read (somewhere after filters), then a copy of the data is cached in ContentCachingRequestWrapper.
In my case issue was the next: Request body was started to be converted by
MappingJackson2HttpMessageConverter. Reading of request body and converting to the Java Object happened in a gradual way: first part (8000bytes) read - first part converted, ... . Then conversion exception happened and then I got only 8000bytes in a controller exception handler (#ExceptionHandler) - part of the request body that was read by a converter.

Spring Session not working on Tomcat 8 when using Tiles - SESSION Cookie is not set as response is already included

I am using Spring Session 1.2.0.RELEASE on a Spring Boot Project. This is packaged as a war and deployed on Tomcat 8.
I have followed Spring Session documentation and configured it properly. The problem is that the entry point to the application is a controller that sets some value on session but the SESSION cookie is not sent to the browser.
Debugging I see that:
org.springframework.session.web.http.CookieHttpSessionStrategy.onNewSession() tries to write the cookie:
this.cookieSerializer
.writeCookieValue(new CookieValue(request, response, cookieValue));
org.springframework.session.web.http.DefaultCookieSerializer.writeCookieValue() sets the cookie in the response:
response.addCookie(sessionCookie);
The cookie isn't actually written. The underlying response object is org.apache.catalina.core.ApplicationHttpResponse. Its addCookie() method is:
/**
* Disallow <code>addCookie()</code> calls on an included response.
* #param cookie The new cookie
*/
#Override
public void addCookie(Cookie cookie) {
if (!included)
((HttpServletResponse) getResponse()).addCookie(cookie);
}
The problem is that included attribute, which at some point is set true, preventing the cookie from being added.
This happens when the jsp (using tiles) is being serviced:
UPDATE:
This is the moment when the response is being marked as included (when standard.jsp tiles layout is inserting an attribute:
<tiles:insertAttribute name="header" ignore="false"/>
To work around this problem I ended up creating a filter to enforce the creation of the session.
As seen, the first call to the controller didn't add the cookie because during the Tiles-JSP rendering the response was already marked as included. What I do is forcing the creation of the session in the filter and redirecting asking the very same requestURI. This way, since the call doesn't involve a tiles rendering the cookie is created and can be used right away in the next calls.
#Bean
#ConditionalOnExpression("${sessionEnforcerFilter.enabled:true}")
public FilterRegistrationBean sessionEnforcerFilter(){
logger.info("Registering sessionEnforcerFilter");
FilterRegistrationBean frb = new FilterRegistrationBean();
frb.setName("sessionEnforcerFilter");
frb.setFilter(new SessionEnforcerFilter());
frb.setUrlPatterns(Arrays.asList(new String[]{"/*"}));
return frb;
}
public class SessionEnforcerFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
if(httpServletRequest.getSession(false)==null){
logger.debug("sessionEnforcerFilter.doFilter () - Session is null - forcing its creation");
httpServletRequest.getSession();
String requestURI = httpServletRequest.getRequestURI();
logger.debug("sessionEnforcerFilter.doFilter () - Repeating request [{}]", requestURI);
httpServletResponse.sendRedirect(requestURI);
}else{
chain.doFilter(httpServletRequest, response);
}
}
#Override
public void destroy() {}
}
summary
Hold breakPoint in SessionRepositoryResponseWrapper.onResponseCommitted().
Check that the response object inside the SessionRepositoryRequestWrapper is a non-wrapped response. (included = false)
If it is a wrapped response object, make sure that the sessionRepositoryFilter comes first.
================
Spring-session is already handling the problem when 'DispatcherType.INCLUDE (included = true)'.
SessionRepositoryResponseWrapper.onResponseCommitted() is trying to addCookie to the original response object.
The sessionRepositoryFilter must be in the first position to wrap the original applicationHttpResponse passed by tomcat.
Problem Situation
The SessionRepositoryRequestWrapper receives the wrapped response and holds it.
When executing doInclude() in the servlet container, find the original reponse and wrap it with ApplicationHttpResponse (included = true).
Then, SetResponse (new wrapping response) to the innermost wrapper.
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.doInclude%28javax.servlet.ServletRequest%2Cjavax.servlet.ServletResponse%29
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.wrapResponse%28org.apache.catalina.core.ApplicationDispatcher.State%29
Spring-session does an addCookie on the response (expecting the original response) stored in SessionRepositoryResponseWrapper.onResponseCommitted(), but it can not because it is set to 'included = true'.

Alter request header before controller method using #RequestHeader is called

I have a few controllers that use the #RequestHeader annotation to get the logged in user. I cannot seem to find the correct way to alter the header before the method is called.
I already tried to wrap the request and using an interceptor (in the prehandle method, if I'm not mistaken) and pass along the request,but it seems like the headers are not being queried. An exception is thrown by the servlet dispatcher that the username is missing in the header.
Therefore I'm wondering whether someone knows how and when spring handles this annotation, so I can write the proper interceptor.
Don't have the code at hand, but if needed, I will post fragments later on. But the question is simple: how to inject a param into the request header when #RequestHeader is used on a controller method?
Kind regards,
Tom
First edit:
#Sotirios
I tried using the Filter, which works. But this is less convenient for me than the handlerinterceptor. Since I only need the filter for debugging. So again the better question: why isn't this not working with an interceptor?
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public Enumeration getHeaders(String name) {
Enumeration headers = super.getHeaders(name);
if( isUseFilter() && Constants.REMOTE_USER.equalsIgnoreCase(name) ){
String user = super.getHeader(name);
headers = enumeration(asList(isEmpty(user)? getDebuggingUserId() :user));
}
return headers;
}
};
chain.doFilter(wrapper, response);
}

How to ignore filter for postback calls

I have a filter which requires an id-parameter (GET). This works fine when I add the parameter in the navigation links. But when I stay at the same site (e.g. ajax calls), the filter shouts and screams, because the id-parameter is lost.
Is there a chance to ignore the filter if the request come from ajax?
something like
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (FacesContext.getCurrentInstance().isPostback()) {...}
...
}
?
I found a solution which works fine
private boolean isAJAXRequest(HttpServletRequest request) {
boolean check = false;
String facesRequest = request.getHeader("Faces-Request");
if (facesRequest != null && facesRequest.equals("partial/ajax")) {
check = true;
}
return check;
}
in the beginning of the filter, just add it like:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
if(isAJAXRequest(req)){
System.out.println("IGNORE THIS FILTER, AJAX");
chain.doFilter(request, response);
return;
}
...
}
You have to define the functional requirement more clearly. You are currently too vague and mixing several concepts.
What exactly do you want to check on?
It is a GET request.
It is not a JSF ajax POST request.
It is not a POST request.
Your current question title "How to ignore filter for postback calls" covers 2 and 3. Your current question body "which requires an id-parameter (GET)" covers 1. Your own answer covers 2.
This is really not clear. So, I'll just show how to cover each of them:
To check if it's a GET request:
if ("GET".equals(request.getMethod())) {
// It's a GET request.
}
To check if it's not a JSF ajax POST request:
if (!"partial/ajax".equals(request.getHeader("Faces-Request"))) {
// It's not a JSF ajax request.
}
To check if it's not a POST request:
if (!"POST".equals(request.getMethod())) {
// It's not a POST request.
}
They all are quite different:
This not only excludes POST, but also e.g. PUT and OPTIONS requests.
This doesn't exclude synchronous JSF POST requests (e.g. submit button without <f:ajax>).
This also excludes all other kinds of POST requests.
Think twice and ultimately just use the right tool for the job.
An alternative would be to retain the current GET query string in the form action URL (i.e. solving the problem by its roots instead of workarounding it). This is answered here: Retaining GET request query string parameters on JSF form submit.
This workaround just works:
private boolean isAjax(HttpServletRequest request) {
//Return if current request header is equals to "XMLHttpRequest"
return "XMLHttpRequest".equals((request).getHeader("X-Requested-With"));
}

Resources