Spring Boot ErrorPageFilter block the custom exception handler with #ExceptionHandler - spring-mvc

My web app constructed with SpringBoot 2.0.0.M3 and java8, deploy on outer tomcat/8.0.24 war.
My Exception handler:
#ControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler extends
ResponseEntityExceptionHandler {
#ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handlerMyException(
MyCustomException rae) {
String msg = "MyCustomException";
return new ResponseEntity<>(msg ,
HttpStatus.OK);
}
}
I trow a MyCustomException in my Controller but it wasn't handled by handlerMyException() method:
ERROR 1 --- [ajp-nio-8009-exec-372] o.s.b.w.servlet.support.ErrorPageFilter : Forwarding to error page from request [/api/test] due to exception
Then I disable the ErrorPageFilter by:
setRegisterErrorPageFilter(false);
this time no error log occurred, but handlerMyException() still not triggered. Is anyone know this? TIA!

Related

Setting custom date formats through Jackson2ObjectMapperBuilder causing request processing to continue after exception

I have a MockMvc test for testing that a JSON payload to a controller is validated and a HTTP 400 (bad request) is rendered for org.springframework.data.mapping.PropertyReferenceException and org.springframework.http.converter.HttpMessageConversionException.
The respective exception handlers are implemented as follows.
#ControllerAdvice
public class LocalExceptionHandler {
#ExceptionHandler(PropertyReferenceException.class)
public ResponseEntity<Object> handlePropertyReferenceException(PropertyReferenceException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(HttpMessageConversionException.class)
public ResponseEntity<Object> handleHttpMessageConversionException(HttpMessageConversionException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
I'm using a Spock specification for implementing the test. The setup is as follows.
MockMvc mvc
public JsonSerializer[] buildJsonSerializers() {
return new JsonSerializer[]{new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)),
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))};
}
Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat(DATE_TIME_FORMAT);
builder.serializers(buildJsonSerializers());
};
}
protected ObjectMapper buildObjectMapper() {
def objectMapperBuilder = new Jackson2ObjectMapperBuilder()
jsonCustomizer().customize(objectMapperBuilder)
objectMapperBuilder.modules(new MoneyModule()
.withMonetaryAmount(Money::of)
.withAmountFieldName("number")
.withFormattedFieldName("pretty"))
objectMapperBuilder.build()
}
def setup() {
ObjectMapper mapper = buildObjectMapper()
def mockMvcBuilder = MockMvcBuilders
.standaloneSetup(controller)
.setControllerAdvice(LocalExceptionHandler.class)
.setMessageConverters([new MappingJackson2HttpMessageConverter(mapper)]
.toArray(new HttpMessageConverter[1]))
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
mvc = mockMvcBuilder.build()
}
So the above setup just sets the date format through a customizer and then builds the object mapper using the Jackson2ObjectMapperBuilder.
The problem with that setup is that the builder is causing an object mapper configuration that results in a weird MockMvc behaviour.
When posting a bad request to a controller, a proper Exception is thrown and handled by one of the above exception handlers but request processing is not stopped and the controller method is invoked.
When running the production code (as Spring Boot application) error handling is just fine resulting with a HTTP 400.
Just by removing the builder and mimicking just the configuration desired for the test (which is a proper date time format) the test works as expected and request processing is stopped after exception handling.
So basically instead of using the builder I do
def mapper = new ObjectMapper()
mapper.registerModule(new MoneyModule()
.withMonetaryAmount(Money::of)
.withAmountFieldName("number")
.withFormattedFieldName("pretty"))
SimpleModule serializerModule = new SimpleModule()
Arrays.asList(buildJsonSerializers())
.forEach({ s -> serializerModule.addSerializer(s.handledType(), s) })
mapper.registerModule(serializerModule)
So it really looks like the builder is adding some configuration that MockMvc doesn't really deal with properly.
Would appreciate hints on resolving this.

Error managment in ControllerAdvice lead to duplication in URI and 404

I have a simple rest controller :
#RestController
#RequestMapping("/api/v1/")
public class OrderController {
#RequestMapping(value = "/orders2", method = RequestMethod.POST)
public OrderDto createOrder2(#RequestBody OrderDto order) throws Exception {
throw new Exception("Bouh!");
}
}
And I want to manage exceptions globally. From what I read it can be done with something like :
#ControllerAdvice
public class ErrorController {
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ErrorDto handleConflict(HttpServletRequest request, Exception e) throws Exception {
ErrorDto o = new ErrorDto ();
o.setMessage(e.getMessage());
return o;
}
}
But when I make a post on my request, I get the following error :
26/10/2016 17:26:08.187 [http-nio-8080-exec-12] WARN o.s.web.servlet.PageNotFound -
No mapping found for HTTP request with URI [/duorest/api/v1/api/v1/orders2]
in DispatcherServlet with name 'rest'
I don't know why the uri change to /duorest/api/v1/api/v1/orders2
Some facts :
I checked in debug, my code is executed
If I move the method in the rest controller, I get no error and what I expect (my ErrorDto object)
Spring framework version 4.3.3.RELEASE
Spring-data-rest-webmvc version 2.5.4.RELEASE
Anybody already had this problem ? Or any hint ?
Is it resolved? if not please try to execute with #ResponseBody is missing on the handleConflict method.

Error response in json format for Spring interceptor

I am writing a REST based web service. I need to return all the responses as JSON format. I have an interceptor to validate my authentication parameters. On authentication failure scenario, I have to return the error response in JSON format.
Currently i am doing
response.setHeader("Content-Type","application/json");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "{\"error\":\"Missing Authentication Parameters\"}");
The response body is coming as below.
JBoss Web/2.1.3.GA - Error report HTTP Status 401 - {"error":"Missing Authentication Parameters"}type Status reportmessage {"error":"Missing Authentication Parameters"}description This request requires HTTP authentication ({"error":"Missing Authentication Parameters"}).JBoss Web/2.1.3.GA
I need just the JSON string in response. Please help me.
You should probably be using spring-security for this. If you want to do it by hand, an alternative to using sendError on the response is to use spring MVC's #ExceptionHandler along with content negotiation to return JSON.
First define an error class*:
public class Error {
public message;
public exception;
public Error(String message, Exception ex) {
this.message = message;
this.exception = ex;
}
}
And an exception:
public class NotAuthenticatedException extends Exception {
// ...
}
Then in your controller you throw an exception at the appropriate time, catch it with #ExceptionHandler and return a ResponseEntity containing an Error instance and the appropriate error code.
#Controller
public class SimpleController {
#RequestMapping(...)
public String aMethod() {
// ...
throw new NotAuthenticatedException("Missing Authentication Parameters");
}
#ExceptionHandler(NotAuthenticatedException.class)
public ResponseEntity<Error> handleNotAuthenticatedException(
NotAuthenticatedException ex,
HttpServletRequest request) {
return new ResponseEntity<Error>(
new Error(ex.getMessage(), ex),
HttpStatus.UNAUTHORIZED
);
}
}
*use getters/setters to please the java convention gods

Returning an error and message from a Spring controller or service

I'm migrating some servlets over to the Spring framework, using Spring MVC. Currently in each servlet we authenticate the user and if the authentication fails we do this:
if (authfailed)
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"You are not authorized.");
return;
}
On the front end is a YUI-based application, and when an error status is returned the "failure" callback displays a dialog with the error message given above.
I know in my controller I can get the response object and call sendError, but is that the best way to handle this? sendError also throws an IOException so I'd have to catch that - a bit of annoying code to insert in every method of every controller.
I have the same problem handling exceptions - the servlets have try-catch blocks that call sendError in the catch method. I know I can mark my exception handlers with
#ExceptionHandler
#ResponseStatus(value = HttpStatus.NOT_FOUND)
but doesn't the exception handling class need to be in each controller class?
Finally, if the exception happens in a service called from a controller, does the exception bubble up to the controller or should I handle the exception in the service (thus pushing these exception handling issues into the service layer)?
This seems more difficult than it should be, but as with many things in Spring it's likely I don't understand what's going on. All I want to do is to send an error status and message back in the response!
Thanks,
Paul
It looks like you have the most of the answers in your question itself :)
To reiterate,
Have the controller like this
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request) throws NotFoundException {
String id = request.getParameter("id");
if (id == null)
throw new NotFoundException("Id not found in the request");
return "success";
}
Declare the exception class in NotFoundException.java,
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Id Not Found")
public class NotFoundException extends Exception {
public NotFoundException(String msg) {
super(msg);
}
}
This exception class need not be every controller class. Declare it as public class and import it in every required controller.
This is one way of doing it. If you like the non-spring style, declare HttpServletResponse in every controller arguments and do
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request, HttpServletResponse response) {
...
try {
response.sendError(..)
catch(..) {}
}
Or you can use views to show error message,
#RequestMapping("/test")
public String verifyAuth(HttpServletRequest request, Map<String, Object> map){
String id = request.getParameter("id");
if (id == null) {
map.put("status", HttpStatus.NOTFOUND);
map.put("reason", "Id Not Found");
return "error"
}
return "success";
}
Make sure your viewResolver is configured correctly and in the error.jsp to get the error string, you could say.
<body>
${status} ${reason}
</body>
Define error.jsp with nice css for all kind of errors you would expect.
These are not the only ways. With spring you have freedom to do anything. I have seen few ppl rendering json object for error message.
To answer your another question of if the error happens in the service called by the controller is depend on your scenario. For example you are trying to read the user store, if the user store not available error happens, I would handle there itself to read from another replica user store if one available and If I found user does not exist I would leave the exception to the controller to throw.

flex: Unhandled AsyncErrorEvent when connecting to the server

I've created a custom class to handle method calls from the server and I get this error
Error #2044: Unhandled AsyncErrorEvent:. text=Error #2095: flash.net.NetConnection was unable to invoke callback close. error=ReferenceError: Error #1069: Property close not found on MyClient and there is no default value.
code from function that does the connection:
myClient = new MyClient();
myClient.addEventListener(HearEvent.HEARD_SOMETHING,onHear);
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, ncOnStatus);
nc.client = dasClient;
nc.connect(connectStr.text, p1.text, p2.text, int(p3.text), p4.text);
that's the MyClient class
public class MyClient extends EventDispatcher
{
public function hear(s:String):void
{
trace(s);
dispatchEvent(new HearEvent(s, HearEvent.HEARD_SOMETHING));
}
}
Depending on your requirements, you can either ignore this error by handling the AsyncErrorEvent in an empty function or prevent the error from happening by adding a close method to the MyClient that performs appropriate action.

Resources