Why Spring Boot inject same HttpServletResponse object to my Controller method for different request? - spring

I wonder why spring boot inject same response object to my controller method parameter for different request, i use it like follow:
#Controller
#Slf4j
#Profile("default")
#RequestMapping("/test")
public class TestController {
#RequestMapping("/test")
#ResponseBody
public void getDeviceImage(#RequestParam("serialNumber") String serialNumber, HttpServletResponse response) {
return balabala;
}
}
I add a breakpoint before return command, and i find that response object's address is same for different request.
I want to write some thing to response.getOutputStream(), and i found there exists previous buffered data.

HttpServletResponse can be used if you need to add some extra meta information like cookies etc. By default even if you don't specify HttpServletResponse in the arguments, in typical MVC, model is added to the newly constructed response before propagating to the view.
If you just need to return some response back, say a model or entity or a simple JSON, you don't have to manually mess the HttpServletResponse. Unless you want to dig through cookies or headers etc.,. In your code, if you don't need to care about this, you might probably not need it.
As per the API doc for HttpServletResponse:
The servlet container creates an HttpServletResponse object and passes
it as an argument to the servlet's service methods (doGet, doPost,
etc).
What you see is probably the default configurations that Spring sets up.
With #ResponseBody, the return type is directly written back to response.
https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

Finally, i find Response will been reused all the time, and Response's recycle method will been invoked for each request(org.apache.catalina.connector.Response#recycle).
But by default facade&outputStream&writer will not been cleaned, so i make system property "org.apache.catalina.connector.RECYCLE_FACADES" to be "true", after that issue disappears.

Related

How to intercept GET call with Spring Data Rest?

I'm using spring data rest with #RepositoryRestResource where all the verbs are automatically handled for all the entities in the system.
There is no controller necessary for my project.
But I do want to perform certain action before the GET call is made to an entity. What is the best way to do this without writing a custom controller?
There are event handlers I can write in Spring Data Rest like #HandleAfterDelete but there are not handlers for GET.
I'm afraid there is currently no solution which would provide this out of the framework itself. However, there is a pull request which was discussed but not yet implemented as there are still open questions with regard to the universality of findBy* methods.
In case you do not need a that general solution the yet suggested HandlerInterceptor is the way to go…
public class YourInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
// decide on request.getMethod() what to do next
}
}

Having #RequestBody and HttpServletRequest in the same Controller method

I have a situation where i have to update the request before i process it. basically, I will have to update the request with a property which can change with time. It is not practical to ask the consumer send the property along with request.. So, I feel like the only way to do it is.. update the request in controller method before processing it..
#RequestMapping(value="/**", method=RequestMethod.POST)
public void processRequest(HttpServletRequest servletRequest, HttpServletResponse response )
Untill now, HttpServletRequest is used in the controller method. the request is then read as stream and then converted to json string to get processed. I am thinking to replace it with #RequestBody and have the request read into a pojo, then update, then process. But, I also need the pathInfo from HttpServletRequest to identify which uri i need to process the request for..
So, Can i use both #RequestBody and HttpServletRequest in the same method??
I tried it and i dint see any issues.. But, I wanted to check if there is any thing i am missing..
#RequestMapping(value="/**", method=RequestMethod.POST)
public void processRequest(#RequestBody final DateRequest request, HttpServletRequest servletRequest, HttpServletResponse response )
Please advise..
This is fine and supported by Spring. As a matter of fact, I just finished writing a controller method virtually identical to the one you use in your example. Spring is very flexible with what is allowed for a method decorated with #RequestMapping. According to the documentation:
#RequestMapping handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values. ... The table below shows supported controller method arguments.
[ See documentation for complete table ]
In the table of acceptable argument types, it specifically allows HttpServletRequest, HttpServletResponse, and objects annotated with #RequestBody.
So you should be set, according to your findings, the fact that other people are doing this, and the documentation specifically mentioning it.

Spring calling controller method based on user information

Suppose there are two type of roles in the application -
Admin
Zonal Manager
Admins can get all the office ids while the zonal managers can get only the office assigned under his zone. In the controller I want something like this
#RequestMapping(method = RequestMethod.GET)
Collection<Long> getOfficeIds(){
// returns all office ids in system
}
#RequestMapping(method = RequestMethod.GET, value = "/{zoneId}")
Collection<Long> getOfficeIds(#RequestParam("zoneId") long zoneId){
// returns all office ids in the zone
}
Now I want all my users to make request with the no-arg version only (the first method). The system should get user role before hitting controller and should call appropriate controller method (if admin then call the first method, if zonal manager call the second one with appropriate zone).
The question is , is it possible at all ? If yes then what would be the best way of doing this ? I could try to modify the request in a servlet filter. Is there a way using method argument resolver ?
Per comments, I am posting the answer below.
The best thing to do to achieve your goal is to add a filter which runs before the request is handled by the controller. In this filter, you can apply the appropriate logic to determine the requesting user's role and act accordingly. If you follow the same URL pattern in all of your controllers to handle these different cases, you can simply rewrite the internal URL after determining which case to apply so that it can be handled by the appropriate controller. In this way, you can keep all of your user-role logic in one location and your controller logic can handle their own, separate flows accordingly.
To create such a filter using spring, you may do something like the following:
#Component("accountContextFilter") public class AccountContextFilter extends OncePerRequestFilter {
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException{
//user role and routing logic
}
}
The reference for the logic inside doFilterInternal can be found here: How to use a servlet filter in Java to change an incoming servlet request url?
Simply change the request path accordingly by appending to your route with the role-defined URLs and you're done.

Initialize Singletons in Spring Framework 3 MVC

I am writing Spring 3.1.0 MVC based application. The problem is: i want to put some objects in a singleton object (current HttpServletRequest and HttpSevletResponse) to use them in other objects(Spring Controllers). But couldn't do so. I tried to extend DispatcherServlet, overriding both doService and doDispatch. Also tried to implement own HandlerInterceptor. No result.
Where can I initialize my singleton objects? And where is Spring Frameworks's entry point and destroy point (i.e. like init() and destroy() methods or lifecycle)?
The current HttpServletRequest and HttpServletResponse are available as method arguments to your controller methods:
#RequestMapping("/foo")
public String foo(HttpServletRequest request) {
}
I believe you can also #Inject them in your controller. A proxy will be injected, and each time you refer to them the current ones will be used. (I'm not 100% certain about this one)
A third option is to use the RequestContextHolder container, and get everything from there.

Spring WebMVC: interceptor which has access to the method definition and the HttpServletRequest

I'm trying to intercept Spring Controller calls which are annotated, similar to:
#RequestMapping("/my/page")
#AccessRestriction(module = Module.Audit, action = AuditActions.Log)
public ModelAndView myPage() {
// pls type teh codez
}
At this point I want to access both the values of the #AccessRestriction method, the HttpServletRequest object to check if the values match the restrictions and the HttpServletResponse object in order to send a redirect , if applicable. Being able to throw an exception might be suitable as well.
I've looked into Interceptors but they don't offer access to the method, just the handler. What are my options of achieving this?
My suggestion would be to decouple the two concerns, one to check the annotation and throw an exception, another to catch that exception and translate it into a redirect.
The first concern could be done using the Auto-proxy facility, which would apply an AOP-style interceptor to any invocations on your controller objects. They would check for the annotation, validate the invocation, and throw a custom RuntimeException is the conditions are violated.
You could then have a custom HandlerInterceptor which checked for this exception in the afterCompletion method, sending a redirect via the response object if it's present.

Resources