Spring aspects forward and cancel previous request - spring

Here is my question
I want to intercept request before any spring controller call, check and modify request URI. After that it has to call another SPRING controller method.
I used mvc:interceptors however I want to configure it with annotations thats why I need a solution for #Aspect. Everything is working but controller called twice, for the original request and for the new request. Using interceptors I return false and it cancels it, how do I do about Aspect classes? Thank you
Here is my code:
#Component
#Aspect
public class TestAspect {
#Before("execution(* mycontroller.*(..)) &&" + "args(request,response)")
public void interceptUrl(HttpServletRequest request, HttpServletResponse response) {
System.out.println("#Aspect is running!");
System.out.println(request.getRequestURI());
if (request.getAttribute("client") == null) {
request.setAttribute("client", "test");
request.getRequestDispatcher("/newpath/contact").forward(request, response);
}
}
}

You should consider #Around advice instead of #Before. In this case you can simply not execute original request chain.

Related

Debug Jersey mapping/routing execution before reaching endpoints/resources

I have been working with Glassfish/Jackson for over a year and I always have this problem when introducing a new endpoint implementation: when the endpoint is not reached and I want to understand why, the only hints I have to go on are the returned request, since the execution doesn't reach the desired endpoint or resource (routing/mapping error).
I want to intercept the Jersey mapping/routing execution before reaching endpoints/resources, with the "raw" request, so that I can better understand resource/endpoint mapping and routing problems.
This answer to a different question, by #xeye, solved this problem for me:
Create a filter that implements ContainerRequestFilter, and override its filter method. This will be where we can intercept all requests for debugging.
// Executed whenever a request is sent to the API server.
// Useful for debugging errors that don't reach the desired endpoint implementation.
#Provider
#Priority(value = 0)
public class MyFilter implements ContainerRequestFilter {
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
try {
// put a breakpoint or log desired attributes of requestContext here.
} catch (Exception e) {
// ignore
}
}
}
Then register this new class in your ConfigResource implementation.
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig(){
register(MyFilter.class);
// ...
}
(It's OK to Ask and Answer Your Own Questions)

Spring controller advice does not correctly handle a CompletableFuture completed exceptionally

I am using Spring Boot 1.5, and I have a controller that executes asynchronously, returning a CompletableFuture<User>.
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private final UserService service;
#GetMapping("/{id}/address")
public CompletableFuture<Address> getAddress(#PathVariable String id) {
return service.findById(id).thenApply(User::getAddress);
}
}
The method UserService.findById can throw a UserNotFoundException. So, I develop dedicated controller advice.
#ControllerAdvice(assignableTypes = UserController .class)
public class UserExceptionAdvice {
#ExceptionHandler(UserNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
public String handleUserNotFoundException(UserNotFoundException ex) {
return ex.getMessage();
}
}
The problem is that tests are not passing returning an HTTP 500 status and not a 404 status in case of an unknown user request to the controller.
What's going on?
The problem is due to how a completed exceptionally CompletableFuture handles the exception in subsequent stages.
As stated in the CompletableFuture javadoc
[..] if a stage's computation terminates abruptly with an (unchecked) exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause. [..]
In my case, the thenApply method creates a new instance of CompletionStage that wraps with a CompletionException the original UserNotFoundException :(
Sadly, the controller advice does not perform any unwrapping operation. Zalando developers also found this problem: Async CompletableFuture append errors
So, it seems to be not a good idea to use CompletableFuture and controller advice to implement asynchronous controllers in Spring.
A partial solution is to remap a CompletableFuture<T> to a DeferredResult<T>. In this blog, an implementation of a possible Adapter was given.
public class DeferredResults {
private DeferredResults() {}
public static <T> DeferredResult<T> from(final CompletableFuture<T> future) {
final DeferredResult<T> deferred = new DeferredResult<>();
future.thenAccept(deferred::setResult);
future.exceptionally(ex -> {
if (ex instanceof CompletionException) {
deferred.setErrorResult(ex.getCause());
} else {
deferred.setErrorResult(ex);
}
return null;
});
return deferred;
}
}
So, my original controller would change to the following.
#GetMapping("/{id}/address")
public DeferredResult<Address> getAddress(#PathVariable String id) {
return DeferredResults.from(service.findById(id).thenApply(User::getAddress));
}
I cannot understand why Spring natively supports CompletableFuture as return values of a controller, but it does not handle correctly in controller advice classes.
Hope it helps.
For those of you who still run into trouble with this : even though Spring correctly unwraps the ExecutionException, it doesn't work if you have a handler for the type "Exception", which gets chosen to handle ExecutionException, and not the handler for the underlying cause.
The solution : create a second ControllerAdvice with the "Exception" handler, and put #Order(Ordered.HIGHEST_PRECEDENCE) on your regular handler. That way, your regular handler will go first, and your second ControllerAdvice will act as a catch all.

Accessing both Request and Response in a Jersey Filter

In Jersey, one can add a ContainerRequestFilter or a ContainerResponseFilter
public class RequestFilter implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
// logic
}
}
public class ResponseFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
// logic
}
}
Both are added using PackagesResourceConfig:
PackagesResourceConfig prc = new PackagesResourceConfig("com.company.xxx");
prc.getContainerRequestFilters().add(new RequestFilter());
prc.getContainerResponseFilters().add(new ResponseFilter());
Although the response filter also has access to the request, it is called AFTER the endpoint is called. What we require is the ability to access the request and the response in the same filter BEFORE the endpoint is called. The request filter is called before the endpoint, but does not have access to the response.
There seems to be no way to add a standard javax.servlet.Filter which has access to both the HttpServletRequest and the HttpServletResponse, and is called before the endpoint is invoked.
Thanks
You cannot filter anything without calling the endpoint, because that's how the request comes to your service. If you meant without hitting the controller, that's a different story. But even then, you want something like pre-matching response filter, which naturally does not exist. It defeats the purpose of your service. Why not just add whatever header you want on all responses after the controller and any other pieces in the handling chain finish their work?! You can access both the request context and the response context in the response filter, so as Paul mentioned in the comments you could store some information you will need in the requestContext while in the ContainerRequestFilter.
JAX-RS spec has some good examples of both filters and interceptors.

Can I get Spring Validation errors in prehandle

I currently have something similar to this in all of my endpoints in my spring app.
if(bindingResult.hasErrors()){
return new ResponseEntity<>(BAD_REQUEST);
}
I would like to move this to a http interceptor so that I only need it in one place. However, I cannot figure out how to get all of the errors from the binding result in preHandle.
How would I get validation errors in preHandle, or some other time before it starts the actual route?
One way to achieve what I think you're looking for is to not include BindingResult as a method parameter. Given no BindingResult is included as a method argument Spring will throw a BindException exception. You can define an ExceptionHandler, generally I've placed these within a #ControllerAdvice, to handle the exception as needed. Below is some sample code
Controller
#PostMapping
public SomeReturnObject someMethod(#Valid SomeCommand command) {
// logic - no longer contains checks for binding result errors
}
As part of ControllerAdvice
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationControllerAdvice {
....
#ExceptionHandler(BindException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
protected SomeResponse handleBindException(BindException ex) {
// handle exception
}
}

Why doesn't Spring MVC throw an error when you POST to a controller action that accepts HTTP GET?

I just noticed a weird problem as I've been testing my application. I was accidentally POSTing to a method that accepts HTTP GET (It was a typo - I'm a little tired), but the weird thing is that Spring was executing a GET action anyway - it wasn't throwing an error.
Here is the mapping for my GET action that I was POSTing to instead:
#RequestMapping(value = "/partialMapping/{partialMappingId}/edit", method = RequestMethod.GET)
public ModelAndView edit(#PathVariable long partialMappingId) {
return new ModelAndView(view("edit"), "partialMapping",
partialMappingService.findPartialMapping(partialMappingId));
}
What I would have expected was for Spring to say, "There is no action called /partialMapping/{partialMappingId}/edit for HTTP POST".
Instead... if you use the HandlerAdapter and pass it "POST" and "/partialMapping/1/edit", it runs my index action instead ("/partialMapping"). It doesn't throw an error. Why?
Is this a bug in spring, or is this desired behaviour? It's not a big deal when it comes to production code, but it surely makes debugging problems harder.
Here is the code I am using to execute a controller action in my tests:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) {
try {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
I found this code per another answer to a question on this site.
I believe your test code mimics the dispatch step that tries to find a matching Controller method signature after the URL and HTTP method have resolved. In other words, you are not testing your controller at the right level if you want to test the HTTP message bindings. For that kind of testing you would probably want to deploy to a server (perhaps embedded Jetty inside your test) and use RestTemplate to call it. That's what I do anyway.
If you annotate with Spring MVC annotations as below
#RequestMapping(method = RequestMethod.GET it should work.

Resources