2013-05-20 4 views
10

У меня возникли проблемы при использовании @ControllerAdvice и @Valid аннотаций вместе в контроллере REST.Spring Rest ErrorHandling @ControllerAdvice/@Valid

У меня есть контроллер остальные объявлены следующим образом:

@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(); 
    } 
} 

А класс ErrorHandler:

@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); 
    } 
} 

Проблема заключается в том, что, если я добавить "бросить новый RuntimeException" в пределах метод RestExample.add, тогда исключение корректно обрабатывается RestErrorHandler класс.

Однако когда керлинг не корректный запрос к контроллеру RestErrorHandler не поймать исключение брошенный валидатор, и я получаю ответ 400 BadRequest. (Для не действительного запроса я имею в виду запрос XML, где Имя пользователь не задан)

Обратите внимание, что XmlRequestUser класса автогенерируемого плагин 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> 

Сгенерированный класс имеет корректные поля @NotNull Annotation on Username и Password.

Моего context.xml очень легко, содержащий только сканер для контроллеров и создания благоприятных MVC: аннотация привода

<context:component-scan base-package="com.aa.rest" /> 
<mvc:annotation-driven /> 

Кто-нибудь знает, как сделать работу @ControllerAdvice и @Valid аннотаций вместе в Контроллер REST?

Заранее спасибо. Антонио

ответ

11

Вы находитесь на правильном пути, но вам необходимо переопределить handleMethodArgumentNotValid() вместо метода handleException(), например.

@ControllerAdvice 
public class RestErrorHandler extends ResponseEntityExceptionHandler { 

    @Override 
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
      MethodArgumentNotValidException ex, 
      HttpHeaders headers, 
      HttpStatus status, 
      WebRequest request) { 

     LOG.error(e); 
     String bodyOfResponse = e.getMessage(); 
     return new ResponseEntity(errorMessage, headers, status); 
    } 
} 

С JavaDoc из MethodArgumentNotValidException:

Exception быть выброшен, когда проверка на спор с аннотацией @Valid терпит неудачу.

Иными словами, при отказе валидации выдается MethodArgumentNotValidException. Он обрабатывается методом handleMethodArgumentNotValid(), предоставляемым ResponseEntityExceptionHandler, который необходимо переопределить, если вы хотите выполнить пользовательскую реализацию.

+0

Спасибо, это именно то, что я искал. –

+1

@matsev Вместо расширения ResponseEntityExceptionHandler и переопределения метода handleMethodArgumentNotValid, не мог ли он просто использовать @ExceptionHandler (MethodArgumentNotValidException.class)? – Stephane

+0

@StephaneEybert Конечно, почему бы и нет. Я решил расширить «ResponseEntityExceptionHandler», потому что он уже был расширен в исходном вопросе. Связано: с помощью аннотации '@ ControllerAdvice' (независимо от того, расширяете ли вы ResponseEntityExceptionHandler или используете аннотацию' @ ExceptionHandler'), вы получаете согласованную обработку ошибок для всех контроллеров. – matsev

1

В дополнение к этому у меня возникла еще одна проблема при рекурсивной проверке.

Образованные классы пропускали @valid аннотацию на других XmlElements.

Причина этого связана с тем, что я неправильно с помощью пространств имен:

Плагин настроен на использование конкретного пространства имен

<arg>-XJsr303Annotations:targetNamespace=http://www.foo.com/bar</arg> 

Так XSD нужно иметь это в качестве целевого пространства имен (например):

<?xml version="1.0"?> 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.foo.com/bar" 
    xmlns:foo="http://www.foo.com/bar" > 

    <xs:complexType name="XmlPassword"> 
     <xs:sequence> 
      <xs:element name="password" type="xs:string" /> 
      <xs:element name="encryption" type="xs:string" /> 
     </xs:sequence> 
    </xs:complexType> 

    <xs:complexType name="XmlToken"> 
     <xs:sequence> 
      <xs:element name="password" type="foo:XmlPassword" /> 
     </xs:sequence> 
    </xs:complexType> 

</xs:schema> 

Сердечные приветы Антонио

Смежные вопросы