Class level request mapping #requestmapping execute some code on every request to that class - spring-mvc

#Controller
#RequestMapping(value = {"user"})
public class UserController {
...
#RequestMapping(value = {"dashboard"})
public String index(HttpServletRequest req, ModelMap map) {
this.objSession = req.getSession(false);
try {
System.out.println(this.objSession.getAttribute("userid"));
I am using Spring 4.2.
Suppose I have this class and I want to check the session object having the attribute userid=1 or not.
I am doing this checking in every methods under this "/user" request.
My query is that if I can avoid this same coding which i am doing before executing any codes of any methods.
Is there any way round to increase code resuability for checking ?
In advance thanks for your time.

You can make use of interceptors by matching the request path.
<mvc:interceptors path-matcher="/someRequest/*">
<bean class="className" autowire="constructor"/>
</mvc:interceptors>
In path-mathcer you can specify one type of url,so that it will execute whatever you require.
Controller:
#Controller
#RequestMapping(value = {"someRequest/user"})
public class UserController {
Interceptor: Before processing someRequest/user you can use a interceptor like below, in 3 ways you can use->afterCompletion ,preHandle,postHandle.
In you case code would be written in preHandle method
public class SomeRequestIntercept implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object,
Exception exception) throws Exception {
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)
throws Exception {
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
System.out.println("before processing someRequest/**");
return true;
}
}

Related

How to create Object in the class implementing HandlerInterceptor to access it from anywhere for that reqest

I am having situation like: in the preHandle() method of the class implementing HandlerInterceptor, i am having sessionId getting in the incoming HttpServletRequest object request. now using this session id i am fetching userInfo from the DB. the same info i have to use somewhere else like service layer to process the request.
It would be very helpful if anyone of you help me out to achieve it. Thanks in advance.
You can use a ThreadLocal to store a reference to a user that will only be accessible to the current thread of execution.
https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html
You can wrap this in a context class so that in your service you can access the current user via the static call: User user = UserContextUtils.getUser();
UserContextUtils:
public class UserContextUtils {
private static final ThreadLocal<User> CONTEXT = new ThreadLocal<>();
public static void setUser(User user) {
CONTEXT.set(user);
}
public static User getUser() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
The Interceptor:
public class MyHandlerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
User user = null;// get user from the database.
UserContextUtils.setUser(user);
return true;
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) {
// as some web servers re-use threads, you must ensure that the
// context is cleared on completion either here or elsewhere.
UserContextUtils.clear();
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
}

Spring validation ConstraintViolationException cannot be caught using ControllerAdvice

I am building APIs with spring rest and trying to validate the input parameters, like this:
#RequestMapping("/myUrl")
#RestController
#Validated
public class MyController {
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "getSomething", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
public myResponse getMarketResult(
#RequestParam #NotNull #NotBlank #NotEmpty String inputParam) {
//...my implementation
}
}
The validation does work in all these scenarios (null, blanks, empty).
MissingServletRequestParameterException is thrown for null, ConstraintViolationException is thrown for blanks/empty.
Controller advice:
#ControllerAdvice
public class ControllerValidationHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(NestedServletException.class)
public ResponseEntity<Object> processValidationError(NestedServletException ex, HttpStatus status, WebRequest request) {
// my implementation
}
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> processValidationError(ConstraintViolationException ex, HttpStatus status, WebRequest request) {
// my implementation
}
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// my implementation
}
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleExpception(HttpServletRequest req, Exception ex, WebRequest request) {
// my implementation
}
However I observe:
ConstraintViolationException is thrown as the cause of org.springframework.web.util.NestedServletException.
ControllerAdvice is unable to catch ConstraintViolationException and NestedServletException (But MissingServletRequestParameterException works), it's now returning a html (instead of json).
The 2 exceptions are not processed by 'handleException' of ResponseEntityExceptionHandler as well.
How do I solve 1 and 2? I'm using Spring version 4.3.1
Edit 1:
One more example, if I have a parameter like this:
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) #NotNull LocalDate localDate
Expcetion is thrown as mentioned in (1) when the request is empty (eg '/myURL/getSomething?localDate='

advise controller method *before* #Valid annotation is handled

I am adding rate-limiting to a restful webservice using Spring MVC 4.1.
I created a #RateLimited annotation that I can apply to controller methods. A Spring AOP aspect intercepts calls to these methods and throws an exception if there have been too many requests:
#Aspect
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class RateLimitingAspect {
#Autowired
private RateLimitService rateLimitService;
#Before("execution(* com.example..*.*(.., javax.servlet.ServletRequest+, ..)) " +
"&& #annotation(com.example.RateLimited)")
public void wait(JoinPoint jp) throws Throwable {
ServletRequest request =
Arrays
.stream(jp.getArgs())
.filter(Objects::nonNull)
.filter(arg -> ServletRequest.class.isAssignableFrom(arg.getClass()))
.map(ServletRequest.class::cast)
.findFirst()
.get();
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedAttempt(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
}
}
This all works perfectly, except when the #RateLimited controller method has parameters marked as #Valid, e.g.:
#RateLimited
#RequestMapping(method = RequestMethod.POST)
public HttpEntity<?> createAccount(
HttpServletRequest request,
#Valid #RequestBody CreateAccountRequestDto dto) {
...
}
The problem: if validation fails, the validator throws MethodArgumentNotValidException, which is handled by an #ExceptionHandler, which returns an error response to the client, never triggering my #Before and therefore bypassing the rate-limiting.
How can I intercept a web request like this in a way that takes precedence over parameter validation?
I've thought of using Spring Interceptors or plain servlet Filters, but they are mapped by simple url-patterns and I need to differentiate by GET/POST/PUT/etc.
I eventually gave up on trying to find an AOP solution and created a Spring Interceptor instead. The interceptor preHandles all requests and watches for requests whose handler is #RateLimited.
#Component
public class RateLimitingInterceptor extends HandlerInterceptorAdapter {
#Autowired
private final RateLimitService rateLimitService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HandlerMethod.class.isAssignableFrom(handler.getClass())) {
rateLimit(request, (HandlerMethod)handler);
}
return super.preHandle(request, response, handler);
}
private void rateLimit(HttpServletRequest request, HandlerMethod handlerMethod) throws TooManyRequestsException {
if (handlerMethod.getMethodAnnotation(RateLimited.class) != null) {
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedInvocation(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
} else {
rateLimitService.recordInvocation(ip);
}
}
}
}
Add the following controller advice in your application.
#ControllerAdvice
public class ApplicationControllerAdvice {
#InitBinder
#RateLimited
protected void activateBeanPropertyAccess(DataBinder dataBinder) {
dataBinder.initBeanPropertyAccess();
}
}
The #RateLimited should call the class RateLimitingAspect. So, after this all the constraints validator will be called.
See if it's feasible for you to implement similar logic for ##AfterThrowing advice as well which will have similar pointcut.

how can i access controller method value in postHandle in spring mvc 's interceptor

#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Controller
public ModeAndView createUser(User user){
}
how can get controller method createUser's parameter user value
in interceptor 's postHandle ???
You cannot directly.
As you see in its parameters, an Interceptor has direct access to the request, the response and the ModelAndView. If you need to have access to the method parameter user, the simplest is to put it in model.
public ModeAndView createUser(User user){
ModelAndView mav = new ModelAndView();
mav.addAttribute("user", user);
...
return mav;
}
Then in interceptor postHandle method, you simply do
User user = (User) modelAndView.getAttribute("user");
You could write an org.springframework.web.servlet.HandlerInterceptor. (or its convenience subclass HandlerInterceptorAdapter)
#See: Spring Reference chapter: 15.4.1 Intercepting requests - the HandlerInterceptor interface
It has the method:
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception;
This method is invoked after the controller is done and before the view is rendered. So you can use it, to add some properties to the ModelMap
An example:
public class VersionAddingHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* The name under which the version is added to the model map.
*/
public static final String VERSION_MODEL_ATTRIBUTE_NAME =
"VersionAddingHandlerInterceptor_version";
/**
* it is my personal implmentation
* I wanted to demonstrate something usefull
*/
private VersionService versionService;
public VersionAddingHandlerInterceptor(final VersionService versionService) {
this.versionService = versionService;
}
#Override
public void postHandle(final HttpServletRequest request,
final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.getModelMap().
addAttribute(VERSION_MODEL_ATTRIBUTE_NAME,
versionService.getVersion());
}
}
}
webmvc-config.xml
<mvc:interceptors>
<bean class="demo.VersionAddingHandlerInterceptor" autowire="constructor" />
</mvc:interceptors>

In spring mvc 3, how to write a cookie while returning a ModelAndView?

My controller method is returning a ModelAndView, but there is also a requirement to write a cookie back to client. Is it possible to do it in Spring? Thanks.
If you add the response as parameter to your handler method (see flexible signatures of #RequestMapping annotated methods – same section for 3.2.x, 4.0.x, 4.1.x, 4.3.x, 5.x.x), you may add the cookie to the response directly:
Kotlin
#RequestMapping(["/example"])
fun exampleHandler(response: HttpServletResponse): ModelAndView {
response.addCookie(Cookie("COOKIENAME", "The cookie's value"))
return ModelAndView("viewname")
}
Java
#RequestMapping("/example")
private ModelAndView exampleHandler(HttpServletResponse response) {
response.addCookie(new Cookie("COOKIENAME", "The cookie's value"));
return new ModelAndView("viewname");
}
Not as part of the ModelAndView, no, but you can add the cookie directly to the HttpServletResponse object that's passed in to your controller method.
You can write a HandlerInterceptor that will take all Cookie instances from your model and generate the appropriate cookie headers. This way you can keep your controllers clean and free from HttpServletResponse.
#Component
public class ModelCookieInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
for (Object value : modelAndView.getModel().values()) {
if (value instanceof Cookie)
res.addCookie((Cookie) value);
}
}
}
}
NB . Don't forget to register the interceptor either with <mvc:interceptors> (XML config) or WebMvcConfigurer.addInterceptors() (Java config).
RustyX's solution in Java 8:
#Component
public class ModelCookieInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception{
if (modelAndView != null) {
modelAndView.getModel().values().stream()
.filter(c -> c instanceof Cookie)
.map(c -> (Cookie) c)
.forEach(res::addCookie);
}
}
}

Resources