2013-05-20 7 views
10

Mam problemy podczas używania @ControllerAdvice i @Valid adnotacji razem w kontrolerze REST.Wiosna Reszta ErrorHandling @ControllerAdvice/@Valid

Mam kontroler reszta zadeklarowana następująco:

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

I klasa 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); 
    } 
} 

Problemem jest to, że jeśli dodać "throw new RuntimeException" obrębie metoda RestExample.add następnie wyjątek jest poprawnie obsługiwany przez klasę RestErrorHandler.

Jednak gdy Curlingu nie ważny wniosek do sterownika RestErrorHandler nie złapać wyjątek rzucony przez walidator i otrzymać odpowiedź 400 BadRequest. (W przypadku nie ważny wniosek Znaczy żądanie xml gdzie Nazwa użytkownika nie jest określona)

Zauważ, że XmlRequestUser klasa wygenerowaną automatycznie przez wtyczkę 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 wygenerowana klasa ma poprawnie pola @NotNull Adnotacja na polu Nazwa użytkownika i Hasło.

Moja context.xml jest bardzo proste, zawierające tylko skaner dla kontrolerów i umożliwiający MVC: adnotacji napędzane

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

Czy ktoś wie jak zrobić pracę @ControllerAdvice i @Valid adnotacji razem w Kontroler REST?

Z góry dziękuję. Antonio

Odpowiedz

11

Jesteś na dobrej drodze, ale musisz zastąpić metodę handleMethodArgumentNotValid() zamiast metody handleException(), np.

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

Z JavaDoc z MethodArgumentNotValidException:

Wyjątkiem być rzucony podczas walidacji na argument z dopiskiem @Valid zawiedzie.

Innymi słowy, MethodArgumentNotValidException jest generowany, gdy sprawdzanie poprawności nie powiedzie się. Jest obsługiwany przez metodę handleMethodArgumentNotValid() dostarczoną przez ResponseEntityExceptionHandler, która musi zostać nadpisana, jeśli chcesz wykonać niestandardową implementację.

+0

Dzięki, właśnie tego szukałem. –

+1

@matsev Zamiast rozszerzać ResponseEntityExceptionHandler i przesłonić metodę handleMethodArgumentNotValid, czy nie mógłby po prostu użyć obiektu @ExceptionHandler (MethodArgumentNotValidException.class)? – Stephane

+0

@StephaneEybert Pewnie, czemu nie. Zdecydowałem się rozszerzyć 'ResponseEntityExceptionHandler', ponieważ zostało ono już rozszerzone w pierwotnym pytaniu. Powiązane: przy użyciu adnotacji '@ ControllerAdvice' (niezależnie od tego, czy rozszerzysz' ResponseEntityExceptionHandler' lub użyjesz adnotacji '@ ExceptionHandler') uzyskasz spójną obsługę błędów dla wszystkich kontrolerów. – matsev

1

Oprócz tego miałem inny problem z rekurencyjną walidacją.

Generowane zajęcia były brakuje @valid adnotacji na inne XmlElements.

Powodem jest to związane z faktem, że został niesłusznie używając nazw:

Wtyczka jest skonfigurowany do korzystania z określonego obszaru nazw

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

Więc XSD trzeba mieć to jako przestrzeni docelowy (na przykład):

<?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> 

Rodzaj odniesieniu Antonio