Issues with HandlerMethod in annotation interceptor - java

public class AnnotationInterceptor extends HandlerInterceptorAdapter implements InitializingBean {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
Api methodAnnotation = method.getMethodAnnotation(Api.class);
if (methodAnnotation != null) {
//..
}
}
}
Gives me: 'HandlerMethod cannot be resolved to a type'
Haven't been able to track down any read up as to why.

Add the following import:
import org.springframework.web.method.HandlerMethod;

Related

Injecting Interceptor jar file to my spring boot projects

I am trying to create a jar file to inject into any of my spring boot project for logging the request details.
I am able to do this in one of my project. You can see the code below.
How to create the jar out of it and how to inject into other projects?
#Component
public class Interceptor extends HandlerInterceptorAdapter {
private static Logger log = LoggerFactory.getLogger(Interceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// log.info("Inside prehandle");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// log.info("Inside postHandle");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("Inside afterCompletion");
sendToLoggerApi(request, response);
}
}
#Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
#Autowired
Interceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(interceptor);
}
}

Testing Annotation based RequestInterceptor

I wanted to do so custom logic(Record the request and response) on some routes. Based on some research I came decided to use AnnotationBased RequestInterceptor. This is my code for interceptor.
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(final HttpServletRequest request,
final HttpServletResponse response,
final Object handler,
final Exception ex) {
if (handler != null && handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
CustomRecord annotation = AnnotationUtils.getAnnotation(handlerMethod.getMethod(), CustomRecord.class);
if (annotation != null) {
// Record Request and Response in a File.
}
}
Now this class is working as expected but I was unable to unit test this function.
I first thought of trying a creating an HandlerMethod Object but I
did not get anywhere.
Second I tried to use PowerMokito. This was my test code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(CustomInterceptor.class)
#PowerMockIgnore("javax.management.*")
public class CustomInterceptorTest {
#Test
public void restAnnotationRecording_negetivecase() {
HandlerMethod mockHandler = mock(HandlerMethod.class);
PowerMockito.mockStatic(AnnotationUtils.class);
when(AnnotationUtils.getAnnotation(mockHandler.getMethod(),
CustomRecord.class).thenReturn(null);
// Verify file is not saved
}
// A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies - - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() methodcannot be saved.
#Test
public void restAnnotationRecording_happycase() {
HandlerMethod mockHandler = mock(HandlerMethod.class);
PowerMockito.mockStatic(AnnotationUtils.class);
when(AnnotationUtils.getAnnotation(mockHandler.getMethod(), CustomRecord.class).thenReturn(mockAnnotation);
// Verify file is saved
}
}
This gives an Error A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies with doReturn|Throw() family of methods.
I wanted to check if there is any method to test the Interceptor. I am a newbie in Java, thanks for help.
You can easily create your own HandlerMethod without mocking. There's a constructor that accepts an Object (the controller) and a Method (the controller method). The easiest way to get a Method is to simply call Class.getMethod(). What you want to do is just create a dummy controller class, and then use that class to get the method. For example
class TestController {
#Custom
public void testMethod() {}
}
Method method = TestController.class.getMethod("testMethod");
TestController controller = new TestController();
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
Custom annotation = handlerMethod.getMethodAnnotation(Custom.class);
It's that easy. Below is a complete test.
public class HandlerInterceptorTest {
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
private #interface Custom {
}
private static class MyHandlerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Custom annotation = handlerMethod.getMethodAnnotation(Custom.class);
if (annotation != null) {
return true;
}
}
return false;
}
}
private static class TestController {
#Custom
public void testMethodWithAnnotation() {}
public void testMethodWithoutAnnotation() {}
}
#Test
public void testMethodWithAnnotation() throws Exception {
Method method = TestController.class.getMethod("testMethodWithAnnotation");
TestController controller = new TestController();
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
MyHandlerInterceptor interceptor = new MyHandlerInterceptor();
boolean result = interceptor.preHandle(null, null, handlerMethod);
assertTrue(result);
}
#Test
public void testMethodWithoutAnnotation() throws Exception {
Method method = TestController.class.getMethod("testMethodWithoutAnnotation");
TestController controller = new TestController();
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
MyHandlerInterceptor interceptor = new MyHandlerInterceptor();
boolean result = interceptor.preHandle(null, null, handlerMethod);
assertFalse(result);
}
}

Intercept null ResponseBody before marshalling response

I've got multiple controllers for RESTful endpoints which currently return null if there's no resource at the endpoint. For instance,
#RequestMapping(method = ReqeustMethod.GET, value = "{id}")
#ResponseBody
public MyResource get(#PathVariable final Long id) {
return this.myService.get(id); // returns null if bad id
}
I want to return a specific, different resource to the client (ErrorResource) when there's no MyResource with the given id. I know I can do that with a separate method with #ExceptionHandler, such as:
#RequestMapping(method = RequestMethod.GET, value = "{id}")
#ResponseBody
public MyResource get(#PathVariable final Long id) {
final MyResource myResource = this.myService.get(id);
if (myResource == null) {
throw new NotFoundException();
}
return myResource;
}
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorResource notFoundException(
final HttpServletRequest request,
final NotFoundException exception) {
final ErrorResource errorResource = new ErrorResource();
errorResource.setStatus(HttpStatus.NOT_FOUND.value());
errorResource.setDeveloperMessage("No resource found at " + request.getRequestURL());
return errorResource;
}
And that's nice. But what I'd really like to be able to do is have some kind of interceptor that figures out for me that whenever an API method is returning a null #ResponseBody, it should instead run the logic in my notFoundException() method. That will make all my controller methods a little cleaner. Is there any way to do that?
It sounds like a job for Spring's HttpMessageConverter.
You can write your own converter by implementing HttpMessageConverter<T> interface.
In your case I would implement a converter of HttpMessageConverter<MyResource> with a null check on the MyResource instance in the write method. If the MyResource instance is null, then build and write your ErrorResource instance.
Here is an example:
import java.io.IOException;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
public class MyResourceConverter implements HttpMessageConverter<MyResource> {
// a real message converter that will respond to ancillary methods and do the actual work
private HttpMessageConverter<Object> delegateConverter;
public MyResourceConverter(HttpMessageConverter<Object> delegateConverter){
this.delegateConverter = delegateConverter;
}
#Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canRead(clazz, mediaType) && MyResource.class.equals(clazz);
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canWrite(clazz, mediaType) && MyResource.class.equals(clazz);
}
#Override
public MyResource read(Class<? extends MyResource> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
return (MyResource) delegateConverter.read(clazz, inputMessage);
}
#Override
public void write(MyResource t, MediaType contentType,
HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
Object result = null;
if(t == null){
result = // build your ErrorResource here
}else{
result = t;
}
delegateConverter.write(result, contentType, outputMessage);
}
}
Note that this converter needs to be registered in your Spring configuration.
The configuration class must extend WebMvcConfigurerAdapter and override the configureMessageConverters method, like:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// Here we add our custom-configured HttpMessageConverters.
// yourDelegateConverter may be a MappingJackson2HttpMessageConverter instance for example
converters.add(new EmployeeConverter(yourDelegateConverter));
super.configureMessageConverters(converters);
}
References (from official Spring documentation):
HTTP Message conversion
HttpMessageConverter
WebMvcConfigurerAdapter

spring mvc: how to add custom annotion on parameters to controllers

This is my controller class:
#Component
#RequestMapping("/test")
public class MyController
{
#RequestMapping(value = {"test"}, method = RequestMethod.GET)
#ResponseBody
public String test(#MyAnnotation String myValue)
{
return "myValue:"+myValue;
}
}
Moreover, below is my interceptor class:
public class MyInterceptor implements HandlerInterceptor
{
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
MyAnnotation annotation = ((HandlerMethod)handler).getMethod().getAnnotation(MyAnnotation.class);
// TODO
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
{
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception
{
}
}
if I want myValue to be "test", then how to implement the custom annotation #MyAnnotation? what to do in class MyInterceptor

Spring MVC - Redirect inside the Constructor

I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?
For example: Inside the constructor I need to check some condition and if doesn't met I want to redirect to some other place. At the same way the rest of the constructor will not be executed neither the "original following action". I want the code like this.
#Controller
class SampleController{
public SampleController(){
if(![some condition]){
...redirecting code...
}
...rest code...
}
...rest code...
}
EDIT
If constructor is not a good option or approach then is there any option like before filter that will execute always before every action of a constructor and will redirect on the failure of some conditions?
You could use an interceptor:
public class CheckInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
if (handler instanceof TheController) {
// or for any controller: if (handler.getClass().isAnnotationPresent(Controller.class))
if (!check()) {
redirect("/check-failure.html");
return false;
}
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
private void redirect(HttpServletRequest request, HttpServletResponse response, String path) throws ServletException {
try {
response.sendRedirect(request.getContextPath() + path);
} catch (IOException e) {
throw new ServletException(e);
}
}
private boolean check() {
return ...
}
}
Then register it within the applicationContext.xml:
<mvc:interceptors>
<bean class="your.package.CheckInterceptor" />
</mvc:interceptors>

Categories