I am working on an annotation that will be sending some audit events to the other microservice.
Say, I am creating an entity and I have a method add on my Rest controller.
#PostMapping
#Audit
public ResponseEntity<EntityDTO> add(EntityDTO entity){
...
}
I have an appropriate Aspect defined, that is associated with the #Audit annotation.
But here is a trick, the nature of an audit event dictates that I need to extract some metadata from the HttpServletRequest itself.
And I do not want to modify my signature by adding (or replacing my only argument) HttpServletRequest object.
How can I pass HttpServletRequest into my aspect? Is there some elegant way?
Since you're using spring MVC, consider Spring MVC interceptors instead of "generic" aspects.
These are natively supported by Spring MVC and can provide access to both the handler and HttpServletRequest object
See this tutorial for using the interceptors and general configuration
See This thread for some information about the handler
final HandlerMethod handlerMethod = (HandlerMethod) handler; // this is what you'll get in the methods of the interceptor in the form of Object
final Method method = handlerMethod.getMethod();
Following is how it can be done with Spring AOP .
Example annotation.
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
public #interface Audit {
String detail();
}
And the corresponding aspect
#Component
#Aspect
public class AuditAspect {
#Around("#annotation(audit) && within(com.package.web.controller..*)")
public Object audit(ProceedingJoinPoint pjp, Audit audit) throws Throwable {
// Get the annotation detail
String detail = audit.detail();
Object obj = null;
//Get the HttpServletRequest currently bound to the thread.
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
// audit code
System.out.println(detail);
System.out.println(request.getContextPath());
obj = pjp.proceed();
} catch (Exception e) {
// Log Exception;
}
// audit code
return obj;
}
}
NB : Op has accepted interceptor based answer. This answer is to demonstrate the Spring AOP code to achieve the requirement.
Hope this helps
Related
How can I intercept controller methods in Spring MVC and fiddle around with the given parameters passed by the Spring framework?
Example
#PostMapping(consumes = "multipart/form-data")
public ResponseEntity<?> createPerson( CreatePersonCommand command ) {
// ...
}
I want to be able to fiddle with the command object before my createPerson method is called. Lets say, because I want to set additional parameters (e.g. timestamp,...)
Update:
I am aware of the HandlerIntercaptor & Adapter. However, I still dont know how can I fiddle around with the CreatePersonCommand object in the preHandle Method.
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler )
throws Exception {
// FIDDLE WITH CreatePersonCommand ???
return true;
}
You can use Interceptors, method preHandle():
https://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/
Update:
I don't know how to do it exactly but you can try to use your handler object:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
((HandlerMethod)handler).getMethodParameters()...
}
Let me know your final solution :)
With Spring you could use aspects, and easy example for your case would be this:
#Aspect
#Component
public class PreHandlerAspect {
#Before("execution(* com.your.controller..*Controller.*(..))")
public void beforeController(JoinPoint joinPoint){
//Here your code
//You could access to the request, parameters, add the aspect for just 1 controller, many,...
}
}
I have a controller with method parameter as model say
public Response create(Customer customer){
}
Customer model :customer model looks like
#JsonTypeInfo( use = JsonTypeInfo.Id.NAME,property = "type")
#JsonSubTypes({#Type(value = Config.class, name = "IPC")})
public class Customer(){
private String type; }
From swagger UI if i send type as IPC its works fine, but any other value than IPC throws an 400 exception while binding.How can i catch this exception inside controller
try to use the #ExceptionHandler annotation
The documentation of Spring 4 (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-exceptionhandler) states that
The HandlerExceptionResolver interface and the
SimpleMappingExceptionResolver implementations allow you to map
Exceptions to specific views declaratively along with some optional
Java logic before forwarding to those views. However, in some cases,
especially when relying on #ResponseBody methods rather than on view
resolution, it may be more convenient to directly set the status of
the response and optionally write error content to the body of the
response.
You can do that with #ExceptionHandler methods. When declared within a
controller such methods apply to exceptions raised by #RequestMapping
methods of that controller (or any of its sub-classes). You can also
declare an #ExceptionHandler method within an #ControllerAdvice class
in which case it handles exceptions from #RequestMapping methods from
many controllers. Below is an example of a controller-local
#ExceptionHandler method:
So, in your controller you can have a method like this
#ExceptionHandler(MethodArgumentNotValidException.class)
public String handleArgumentNotValid(MethodArgumentNotValidException e, ModelMap map, HttpServletRequest request) {
List<ObjectError> errors = e.getBindingResult() .getAllErrors();
//you can get the exception e,
//you can get the request
//handle whatever you want the then return to view
return "your view that you will handle the exception";
}
I have a very simple Spring Boot Application which consists of a main Application.java (with the default main method), a MainController (which has one requestMapping to /login), and a SecurityConfig (with mainly default values).
My problem is with the _csrf support in Groovy Templates. Everything works fine with FreeMarker, but when I switch to GroovyTemplates the _csrf param does not get put into the model.
Is there a bug in the Groovy Templates, something manually I have to do to grab the token, or is there some configuration step I'm missing (although I don't know why it would work for FreeMarker) ?
UPDATE:
I printed this.properties (HashMap) on the login.tpl (Groovy Template) :
{class=class login, out=java.io.BufferedWriter#5e2aead3, model={error=Optional.empty, org.springframework.validation.BindingResult.error=org.springframework.validation.BeanPropertyBindingResult: 0 errors, spring=org.springframework.web.servlet.support.RequestContext#1d99fb33, springMacroRequestContext=org.springframework.web.servlet.support.RequestContext#7fcc5c78}}
The model key in the properties map includes the parameters
I added error in the Controller action using the:
#RequestMapping(value="/login", method = RequestMethod.GET)
public ModelAndView login(#RequestParam Optional<String> error) {
return new ModelAndView("views/login", "error", error);
}
When using the GroovyMarkupView and the GroovyMarkupViewResolver the properties of the view only contain properties available in the model (and some added for Groovy).
To include the request attributes set the exposeRequestAttributes property of the GroovyMarkupViewResolver to true. Ideally this is done by setting the following property in the application.properties.
spring.groovy.template.exposeRequestAttributes=true
However due to this issue that currently isn't possible.
To work around it create a BeanPostProcessor which check if the incoming bean is a GroovyMarkupViewResolver (or AbstractTemplateViewResolver if you want a more general approach). If so set the exposeRequestAttributes to true.
public class TemplateViewResolverPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instance GroovyMarkupViewResolver) {
((GroovyMarkupViewResolver) bean).setExposeRequestAttributes(true);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
After doing that the CsfrToken is available with the key _csfr, be aware that this is the actual CsfrToken.
Another solution is to create a HandlerInterceptor implement the postHandle method and add the _csfr property to the model. That way you can simply add the value of the token instead of the actual token itself. This will work with any view technology used.
public class CsrfAddingInterceptor extends HandlerInterceptorAdapter {
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mav) throws Exception {
CsrfToken token = (CsrfToken) req.getAttribute(CsrfToken.class.getName())
if (token != null) {
mav.addAttribute(token.getParameterName(), token.getToken());
}
}
}
Then add it as an interceptor and you will have the value available.
So the hacked up way to handle this is to manually add the _csrf attribute to your model in your Controller. For example:
model.addAttribute( "_csrf", request.getAttribute("_csrf") );
I wouldn't recommend that if you have lots of views on your server. I'd suggest you follow one of the options from #M-Deinnum. But, for quick testing this works.
I have a basic Spring MVC controller that looks like this:
#Controller
public void MyController {
#RequestMapping("/secret")
public String show() {
return "secret.jsp";
}
}
I am going to have several similar URLs that can only be reached by signed-in users. Since this is a cross-cutting concern, I'd like to use AOP, and I'd like to make this work via annotations. In other words, I'd like to throw a #RequiresLogin annotation on every controller method that needs to be secret.
AnnotationMethodHandlerAdapter supports the concept of interceptors, which seems on the surface like the right way to go for this. However, I want to know which method is going to be invoked so that I can check it for my #RequiresLogin annotation. I see that there's an "Object handler" parameter that's passed in, but I'm not sure how to turn that into a Class and Method that will be invoked.
Ideas?
There are no good ways to get a method signature in the interceptor.
Try to apply a regular AOP advise to your controller, Spring MVC plays well with it as long as target class proxying is used.
As axtavt writes correctly, Spring-AOP works well with controllers if using proxy-target-class. But there is also the possibility of using JDK proxies if you follow some (tedious) conventions:
Working with interface-based #Controller classes
A common pitfall when working with
annotated controller classes happens
when applying functionality that
requires creating a proxy proxy for
the controller object (e.g.
#Transactional methods). Usually you
will introduce an interface for the
controller in order to use JDK dynamic
proxies. To make this work you must
move the #RequestMapping annotations
to the interface as the mapping
mechanism can only "see" the interface
exposed by the proxy. As an
alternative, you may choose to
activate proxy-target-class="true" in
the configuration for the
functionality applied to the
controller (in our transaction
scenario in <tx:annotation-driven />).
Doing so indicates that CGLIB-based
subclass proxies should be used
instead of interface-based JDK
proxies. For more information on
various proxying mechanisms see
Section 7.6, “Proxying mechanisms”.
Source: 15.3.2 Mapping requests with #RequestMapping
While using spring security would be the optimal approach here, you can implement similar functionality using Spring Aspects. Here is an example of using an Aspect to check for a method containing a particular Annotation.
#Aspect
public class MyAspect {
#Around("execution(* com.test.controllers..*.**(..)) && " +
"within(#org.springframework.sterotype.Controller *)")
public Object execute(ProceedingJoinPoint joinPoint) {
Object target = joinPoint.getTarget();
if (target != null) {
Signature tSig = joinPoint.getSignature();
if (tSig instanceof MethodSignature) {
MethodSignature mSig = (MethodSignature) tSig;
Method method = mSig.getMethod();
if (method != null && method.isAnnotationPresent(MyAnnotation.class)) {
// do something
// parameters are available from joinPoint.getArgs();
}
}
}
}
// allow method invocation to continue
return joinPoint.proceed();
}
The format of the #Around advice will be specific to your application. In this example, it checks for any class annotated with Controller in the package com.test.controllers and all subpackages. See http://static.springsource.org/spring/docs/3.0.x/reference/aop.html for additional options.
Good luck!
How about ResolveHandlerMethodInterceptor using reflection.
Below code is experimental and version-dependent(spring 3.0.2).
import java.lang.reflect.Method;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
public class ResolveHandlerMethodInterceptor implements HandlerInterceptor {
public final static String HANDLER_METHOD = "handlerMethod";
// Here is your servlet name
public final static String SERVLET_NAME = "XXXXX";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView)
throws Exception {
Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
System.out.println("postHandle>>>" + handlerMethod);
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception)
throws Exception {
Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
System.out.println("afterCompletion>>>" + handlerMethod);
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
ServletContext servletContext = request.getSession().getServletContext();
String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + SERVLET_NAME;
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext, attrName);
AnnotationMethodHandlerAdapter adapter = context.getBean(AnnotationMethodHandlerAdapter.class);
Method getMethodResolverMethod = adapter.getClass().getDeclaredMethod("getMethodResolver", Object.class);
getMethodResolverMethod.setAccessible(true);
Object servletHandlerMethodResolver = getMethodResolverMethod.invoke(adapter, object);
Method resolveHandlerMethod = servletHandlerMethodResolver.getClass().getMethod("resolveHandlerMethod", HttpServletRequest.class);
resolveHandlerMethod.setAccessible(true);
Method handlerMethod = (Method) resolveHandlerMethod.invoke(servletHandlerMethodResolver, request);
request.setAttribute(HANDLER_METHOD, handlerMethod);
System.out.println("preHandle>>>" + handlerMethod);
return true;
}
}
==reference==
http://toby.epril.com/?p=934
http://www.jarvana.com/jarvana/view/org/springframework/spring-webmvc/3.0.2.RELEASE/spring-webmvc-3.0.2.RELEASE-sources.jar!/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java?format=ok
So, these approaches listed are good, but they all have limitations. The AOP stuff is a good idea, but its limitation is that I need a way to get ahold of the request and response objects if I want to redirect or modify the response. The controller methods don't necessarily need the requests and responses, and requiring that they appear seems inelegant. I could use spring magic to get the request object from the Aspect, but I couldn't find a way to get the response.
Eventually, I came up with a middle way. I used a filter bean to get the request and the response objects and store them in a ThreadLocal. Then I created an aspect that has a reference to that filter, so that it could easily see the request and response objects.
Then I made the aspect wrap around methods based on the annotation, so I didn't even need to check on whether the annotation was present using code.
This combination approach appears to be working perfectly!
The only downside is that I can't figure out a good way to write an integration test that verifies that the aspect is invoked when there's an incoming request to that URL. It's a little scary that removing a single annotation leaves all my tests passing but allows unauthorized users through.
Thanks everybody for the great suggestions!
I have a Spring MVC application trying to use a rich domain model, with the following mapping in the Controller class:
#RequestMapping(value = "/entity", method = RequestMethod.POST)
public String create(#Valid Entity entity, BindingResult result, ModelMap modelMap) {
if (entity== null) throw new IllegalArgumentException("An entity is required");
if (result.hasErrors()) {
modelMap.addAttribute("entity", entity);
return "entity/create";
}
entity.persist();
return "redirect:/entity/" + entity.getId();
}
Before this method gets executed, Spring uses BeanUtils to instantiate a new Entity and populate its fields. It uses this:
...
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
Here's the problem:
My entities are Spring managed beans. The reason for this is to inject DAOs on them. Instead of calling new, I use EntityFactory.createEntity(). When they're retrieved from the database, I have an interceptor that overrides the
public Object instantiate(String entityName, EntityMode entityMode, Serializable id)
method and hooks the factories into that.
So the last piece of the puzzle missing here is how to force Spring to use the factory rather than its own BeanUtils reflective approach? Any suggestions for a clean solution?
Thanks very much in advance.
You can use #ModelAttribute-annotated method to pre-populate the model with your bean. Then data binder will use that bean instead of instantiating the new one. However, this will affect all method of the controller.
#ModelAttribute
public Entity createEntity() { ... }