I'm having trouble when Using @ControllerAdvice and @Valid annotations together in a REST controller.
I have a rest controller declared as follows:
@Controller
public class RestExample {
...
/**
* <XmlRequestUser><username>user1</username><password>password</password><name>Name</name><surname>Surname</surname></XmlRequestUser>
* curl -d "@restAddRequest.xml" -H "Content-Type:text/xml" http://localhost:8080/SpringExamples/servlets/rest/add
*/
@RequestMapping(value="rest/add", method=RequestMethod.POST)
public @ResponseBody String add(@Valid @RequestBody XmlRequestUser xmlUser) {
User user = new User();
user.setUsername(xmlUser.getUsername());
user.setPassword(xmlUser.getPassword());
user.setName(xmlUser.getName());
user.setSurname(xmlUser.getSurname());
// add user to the database
StaticData.users.put(xmlUser.getUsername(), user);
LOG.info("added user " + xmlUser.getUsername());
return "added user " + user.getUsername();
}
}
And an ErrorHandler class:
@ControllerAdvice
public class RestErrorHandler extends ResponseEntityExceptionHandler {
private static Logger LOG = Logger.getLogger(RestErrorHandler.class);
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Object> handleException(final RuntimeException e, WebRequest request) {
LOG.error(e);
String bodyOfResponse = e.getMessage();
return handleExceptionInternal(e, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}
The problem is that, if I add a "throw new RuntimeException" within the method RestExample.add then the exception is correctly handled by RestErrorHandler class.
However when curling a not valid request to the controller the RestErrorHandler doesn't catch the exception thrown by the validator and I receive a 400 BadRequest response. (For not valid request I mean an xml request where Username is not specified)
Note that XmlRequestUser class is autogenerated by the plugin maven-jaxb2-plugin + krasa-jaxb-tools (pom.xml):
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>src/main/xsd</schemaDirectory>
<schemaIncludes>
<include>*.xsd</include>
</schemaIncludes>
<args>
<arg>-XJsr303Annotations</arg>
<arg>-XJsr303Annotations:targetNamespace=http://www.foo.com/bar</arg>
</args>
<plugins>
<plugin>
<groupId>com.github.krasa</groupId>
<artifactId>krasa-jaxb-tools</artifactId>
<version>${krasa-jaxb-tools.version}</version>
</plugin>
</plugins>
</configuration>
</plugin>
The generated class has correctly a @NotNull Annotation on Username and Password fields.
My context.xml is very easy, containing just a scanner for controllers and enabling mvc:annotation-driven
<context:component-scan base-package="com.aa.rest" />
<mvc:annotation-driven />
Does anybody know how to make working @ControllerAdvice and @Valid annotations together in a REST controller?
Thanks in advance. Antonio
In addition to this I had another problem on recursive validation.
The reason for this is related to the fact that I was wrongly using the namespaces:
The plugin is configured to use a particular namespace
So the XSD need to have this as target namespace (e.g.):
Kind regards Antonio
You are on the right track, but you need to override the handleMethodArgumentNotValid() instead of the
handleException()
method, e.g.From the JavaDoc of MethodArgumentNotValidException:
In other words, a
MethodArgumentNotValidException
is thrown when the validation fails. It is handled by thehandleMethodArgumentNotValid()
method provided by theResponseEntityExceptionHandler
, that needs to be overridden if you would like a custom implementation.Better format of return message: