Java SpringMVC Error object on Exception - java

I have a function that is both an HTTP endpoint and a function that I call elsewhere in the Java program:
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody MyObject getObject(final HttpServletRequest request, final HttpServletResponse response)
The problem is in error handling. Functionality I would like on error:
Function called from inside Java: throws exception
Function called from HTTP: returns error json object
Trying to figure out the best way to do it. Thought about just returning a Java.lang.Object, but that's a lot of casting and type checking.

Add a Spring exception handler when calling it from web and then have the handler return your JSON.
Better approach might be to refactor the functionality into a service method then you can have the HTTP call handle the exception in it's own way and internal calls handle it however you want.
Edit
The second approach would be slightly easier to test too.

Related

ExceptionHandler doesn't catch HandlerInterceptor exception if endpoint path is unknown

I have a component that implements the HandlerInterceptor interface, and implements the preHandle method. In this method I retrieve a parameter from the request, and throw an IllegalArgumentException if that parameter is missing.
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String parameter = request.getHeader("parameter123");
if (StringUtils.isEmpty(parameter)) {
throw new IllegalArgumentException("parameter123 not specified");
}
[...]
return true;
}
In another class annotated with #ControllerAdvice, I have a ExceptionHandler that catches the IllegalArgumentExceptions and turns those into a formatted response with HTTP status code 400.
When this is executed by triggering a valid path of my API, everything works just fine. Problems arise when I try to call an invalid/unexisting path of my API. The HandlerInterceptor is called and the exception is thrown but my ExceptionHandler is not triggered and the result is a basic HTTP status code 500 exception. It seems to both override the basic HTTP status 404 mechanism, while also preventing the triggering of my ExceptionHandlers (even an ExceptionHandler on Exception.class doesn't ever get called).
Any explanations regarding this behaviour are welcome ! Thanks
Although this may be an old question, I want to provide an answer for anyone who may come across it in the future.
When you raise an exception in the preHandle method of a HandlerInterceptor, it may be wrapped in another exception called NestedServletException. This is a specific exception thrown by the Spring framework.
It's worth noting that NestedServletException is a runtime exception that occurs when a servlet or filter throws an exception. It encloses the original exception and provides additional information about the location where the exception occurred.

Get request mapping object in spring interceptor, to get actual url string pattern

It might be hard to explain why, but I have this situation where I need to get the request url mapping string of currently requested url.
Like if I have a GET URL as "/Test/x/{number}"
I want to get "/Test/x/{number}" not "/Test/x/1"
can I get the actual declared url string in interceptor?
If this is possible how can I achieve this
You can implement a HanderInterceptor to intercept, pre or post, request and introspect the method being called.
public class LoggingMethodInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(LoggingMethodInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
GetMapping mapping = method.getMethodAnnotation(GetMapping.class);
log.info("URL is {}", Arrays.toString(mapping.value()));
return true;
}
}
This will output, URL is [/hello/{placeholder}]
Full example can be found here, https://github.com/Flaw101/spring-method-interceptor
You could add more logic to introspect only certain methods, certain types of requests etc. etc.
I think that you can get it with reflection and getting #RequestMapping anotations.
for example when you use
#RequestMapping(value = "/Test/x/{number}", method = RequestMethod.GET)
the value is what you are looking for if I got it right!
You only must find the controller class type.
Its possible I think but I didn't test it.
Check this:
In a Spring-mvc interceptor, how can I access to the handler controller method?
First it may be solved if the HandlerMethod was right but if you get cast error then you must get the controller class [I think].
When you get the controller class then you can looking for the method with according #RequestMapping annotation.
So
1- Find the controller class type
2- Search all methods with in the class by reflection
3- Check method annotations with specified url and specified method [GET / POST]
4- select the best candidate
If you have more than two URL parameter this method is not good!

How to test only specific http request method available for controller method in Spring MVC?

I need to check if only specific http method is available for some url.
For example, if there is a controller like this
#Controller
public class FooController {
#RequestMapping(value = "bar", method = RequestMethod.GET)
public void bar() {/*do something*/};
...
}
For controller test I use junit(4.10), spring-test(3.2.10) and easymock(3.1).
If I write test like this
#Test
public void testBar() throws Exception {
mockMvc.perform(post("bar").session(session))
.andExpect(/*some application's default error response*/);
}
it will pass (although test calls post-method, not get-method).
So I'm looking for a proper way to make sure, that my rest resources are only avaiable by request methods specified in documentation. Two solutions came to my mind:
write tests with wrong request methods and somehow check resource is not available
add custom exception resolver to process org.springframework.web.HttpRequestMethodNotSupportedException: Request method '__' not supported and return same application default error response, but with https status 405 Method not allowed.
What would you suggest and how to check in controller test request method?
Thanks in advance.
You would need to check the status of all the request methods, you could do it using andExpect with status().isMethodNotAllowed() or status().isNotFound() depends on your needs:
Examples:
get: mockMvc.perform(get("bar").andExpect(status().isNotFound()) or mockMvc.perform(get("bar").andExpect(status().isMethodNotAllowed())
Do the same same for put, delete, ....

Multiple exception handlers for ResponseBody methods and normal methods

I have a requirement where I want to handle how exceptions are returned. Depending on the type of the controller method if it is annotated with #ResponseBody a json string should be returned. Additionally if it is a String returning method a jsp error page should be returned.
However it seems that I am unable to define two global exception handler (with ControllerAdvice) both which handles Exception.class but one returns a ModelAndView and the other is annotated with #ResponseBody. I get an exception mentioning that it is too ambigious.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous #ExceptionHandler method mapped for [class java.lang.Exception]
The below code is an example of an ideal situation where both scenarios are handled
The methods
#RequestMapping(value = "/{pathValue}/page", method = RequestMethod.GET)
public String getPage(#PathVariable(value="pathValue") String pathValue, ModelMap model) throws Exception {
#RequestMapping(value = "{pathValue}/jsonData", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ModelMap getJson(#PathVariable(value="pathValue") String pathValue) throws Exception {
The Exception Handlers
#ExceptionHandler(Exception.class)
#ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
in the above example the getPage method should be handled by handleBadRequest and getJson should be handled by defaultErrorHandler
Is there any way to configure two global exception handlers which both handle the same Exception class and returns a page or json based on the Controller method type.
Not out of the box I suppose, because AbstractHandlerMethodExceptionResolver cannot do what you want. If I'm not mistaken then interface has no argument that could indicate method which has thrown particular instance of exception.
I remember that creating wrapping implementation (for example aspect) which was configurable with particular exception type has helped me in the past. Such a implementation will wrap every controller's method's call and catch all exceptions just to wrap it in configurable exception type and rethrow. In your case it would be something like JsonSomethingException and ResourceSomethingException.
Another option might be opposite (more low-level) approach. Note that in exception handling method you receive HttpServletRequest instance, so it is possible to browse some data about request. In you case #RequestMapping bases on this data to determinate which method should be called - you can do the same in exception handler! The best (imho) apprach would be to distinguish json and html by Content-Type header of HTTP protocol. In such situation writing single global error handler, which returns appropriate ResponseEntity based on Content-Type will be quite easy. Especially with addition of some json parser (for example Jackson).
Good luck :).

ExceptionHandler with ResponseBody: set ResponseStatus in method body

I have a method to handle a particular class of exceptions in a Spring MVC environment.
The metod (simplified) implementation follows
#ExceptionHandler(AjaxException.class)
#ResponseStatus(value=HttpStatus.BAD_REQUEST)
#ResponseBody
public Exception handleException(AjaxException ex) {
return ex;
}
This is works fine, but to return a different ResponseStatus I have to create a new handling method.
Is it possible to change the response status inside the method body instead of using the #ResponseStatus annotation without changing the return type?
If not, is it possible to achieve the same result changing the return type (maybe serializing the exception class by myself and returning it as a string)?
Add the HttpServletResponse to the method signature and simply call the setStatus method.
#ExceptionHandler(AjaxException.class)
#ResponseBody
public Exception handleException(AjaxException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return ex;
}
Something like that should work.
Easy done, reading a little more carefully the spring documentation.
It is possible to pass the HttpServletResponse as an object parameter. In such object is possible to set the return code. The syntax is as follows:
#ExceptionHandler(AjaxException.class)
#ResponseBody
public AjaxException handleException(AjaxException ex,HttpServletResponse response) {
//test code ahead, not part of the solution
//throw new NullPointerException();
//end of test code
response.setStatus(404);//example
return ex;
}
This will return the json serialization of the exception along with the specified http return code.
EDIT:
I deleted this answer yesterday because this solution didn't seem to work. The problem was a bit trickyer: when you manage an exception this way, if the method annotated with ExceptionHandler throws an exception itself then the thrown exception is ignored and the original exception is thrown instead.
My code was somehow like the solution I posted (it threw exception in the start of the method), so I couldn't see the json output, the standard spring exception handler was fired instead. To resolve I simply trycatched the exception-throwing line and everything was ok.

Categories

Resources