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

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.

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
}
}

Spring controller exception handling locally and globally

I would like some advice on how to achieve the following. I'm not providing code, as my problem is theoretical, but upon request I can. So this is the situation:
I have multiple controllers, each can throw XYException
I have a #ControllerAdvice class, in which I have an #ExceptionHandler watching for XYExceptions. When it happens, it prints out "XY".
In one (and only one) controller, when XYException is thrown, I want to do some additional task (let's say, do something that only that controller can do), and then I want the exception to be "passed on" to the global handler mentioned above.
I know I can catch the exception, do the desired task in catch block, and then re-throw the exception so the global handler can catch it, but what if I have 23 methods in the controller potentially throwing XYExceptions, and I do not want to put try-catch blocks in all 23 methods.
What is the clean Spring way of achieving this?
You could use AOP to achieve that. You'd write a method that intercepts the method inside that controller and when they throw an exception, you're aop method would run and you can do your stuff and then the exception would go in your handler class.
To achieve that you should have a class and anotate it with #Aspect and #Component
then have a method anotated with #AfterThrowing and setting the pointcut which will intercept when the given method throws an exception.
Look into Aspect Oriented Programming and Aspectj for more info.
The easy way to handle this case in ControllerAdvice is checking the stacktrace where the exception originated.
#ExceptionHandler(Exception.class)
public String handleExc(HttpServletRequest req, HttpServletResponse res, Exception e) {
if ( /*Have all null and safe check */ e.getStackTrace()[0].contains("MyController")) {
// Do your exception handling
}
}

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

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.

Good idea using ControllerAdvice to perform REST authentication of a request?

In my current Spring Boot application i seem to hit a wall when trying to implement a REST request filter. My goal with the request filter was to read the header and body part and validate the incoming data and check if it meets the HMAC construction we are using.
So the request filter seemed not to work an alternative solutions is to use #ControllerAdvice.
Then the request validation can be implemented very easy. But i am not sure if it normally seen as an incorrect usage of the #ControllerAdvice annotation.
#ControllerAdvice
public class GenericWebControllerAdvice {
#ModelAttribute
public void authenticationFilter(#RequestHeader(value = "Authorization") String authHeader, #RequestBody String payload) {
// process authentication based on header info and body content
// calculate the hash and check if meets the security settings
// if the hash fails throw an exception that returns a http status code
}
}
Any comments on the solution or alternatives that are better?
No you should do the validation in the controller (ie method with #RequestMapping).
Spring supports JSR 303/349 bean validation. Thus if your request body is a POJO and you have the correct annotation Spring will automatically do the validation for you. There is a tutorial of that here:
http://www.leveluplunch.com/java/tutorials/017-validate-spring-rest-webservice-request/
As for request parameter validation (ie not bean validation) I have had to make my own transfer objects and exception handling. How you do global exception handling is covered in the Spring Reference guide but generally you extend and/or register a org.springframework.web.servlet.handler.SimpleMappingExceptionResolver. Ironically #ControllerAdvice can be used for exception handling but I find it better to extend and register an Exception Resolver. More info can be found here:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers
Edit based on OP comments and edits:
If your doing authentication or some other request based validation/authorization its probably best to use an Interceptor. Reference doc. #ControllerAdvice will probably not work as the request handling is too far a long. That is you want something before databinding happens.

Purpose of throwing exception by Spring controller function

I have come across few Spring contoller's function, which are throwing IOException.
#RequestMapping(method = ***)
#ResponseBody
public List<Offering> getOfferingDetailsList(HttpServletResponse response, #PathVariable("productIds") String productIdString, HttpServletRequest request) throws IOException
I doubt about use of such exception throwing, when no one above is catching and handling it. Is it fine to set response status like "response.setStatus(HttpStatus.SC_NOT_FOUND)" in place of throwing such exception ? What is the standard way of handling exception in controller ?
it is always good to have common exception handling functionality , so that we can make our controller code free from exception handling , by externalize to common exception handling functionality, i have come across this interesting topic for this purpose
http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
and also use a global exception handler that will do that for all the exceptions of all the controller methods. That will centralize the exception handling, prevent massive code duplication, and avoid cluttering your controller methods.
Look at the #ControllerAdvice and #ExceptionHandler annotations.
A fairly standard way of handling exceptions in Spring Controllers is to use #ExceptionHandler.
Check out this post for more details

Resources