2013-05-20 8 views
10

Ich habe Probleme bei der Verwendung von @ControllerAdvice und @Valid Annotationen zusammen in einem REST-Controller.Spring Rest ErrorHandling @ControllerAdvice/@Valid

Ich habe einen Rest-Controller wie folgt deklariert:

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

Und eine Fehlerbehandler Klasse:

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

Das Problem ist, dass, wenn ich hinzufügen, eine "neue Runtime werfen" innerhalb der Methode RestExample.add dann wird die Ausnahme richtig behandelt von RestErrorHandler Klasse.

jedoch, wenn Curling eine nicht gültige Anforderung an die Steuerung des RestErrorHandler nicht die Ausnahme vom Prüfer geworfen verfängt und ich erhalte eine 400 BadRequest Antwort. (Für nicht gültige Anforderung meine ich eine XML-Anfrage in dem Benutzername nicht angegeben ist)

Beachten Sie, dass XmlRequestUser Klasse vom Plugin automatisch generiert wird 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> 

Die Die generierte Klasse hat korrekt eine @NotNull Annotation in den Feldern Benutzername und Passwort.

Mein context.xml ist sehr einfach, nur einen Scanner für Controller enthält, und ermöglicht mvc: Annotation-driven

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

niemanden wissen, wie in einem @ControllerAdvice und @Valid Anmerkungen arbeiten zusammen zu machen REST-Controller?

Vielen Dank im Voraus. Antonio

Antwort

11

Sie sind auf der richtigen Spur, aber Sie müssen die handleMethodArgumentNotValid() anstelle der -Methode, z.

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

Vom JavaDoc von MethodArgumentNotValidException:

Ausnahme ausgelöst werden, wenn die Überprüfung auf einen Streit mit @Valid kommentierten ausfällt.

Mit anderen Worten wird eine MethodArgumentNotValidException ausgelöst, wenn die Validierung fehlschlägt. Es wird von der handleMethodArgumentNotValid()-Methode behandelt, die von der ResponseEntityExceptionHandler bereitgestellt wird, die überschrieben werden muss, wenn Sie eine benutzerdefinierte Implementierung wünschen.

+0

Danke, das ist genau das, was ich gesucht habe. –

+1

@matsev Anstatt ResponseEntityExceptionHandler zu erweitern und die Methode handleMethodArgumentNotValid zu überschreiben, kann er nicht einfach einen @ExceptionHandler (MethodArgumentNotValidException.class) verwenden? – Stephane

+0

@StephaneEybert Sicher, warum nicht. Ich habe entschieden, den ResponseEntityExceptionHandler zu erweitern, weil er in der ursprünglichen Frage bereits erweitert wurde. Related: Mit der '@ ControllerAdvice' Annotation (unabhängig davon, ob Sie den' ResponseEntityExceptionHandler' erweitern oder die '@ ExceptionHandler' Annotation verwenden) erhalten Sie eine konsistente Fehlerbehandlung für alle Controller. – matsev

1

Zusätzlich hatte ich ein anderes Problem bei der rekursiven Validierung.

Die generierten Klassen wurden eine auf den anderen XmlElements @valid Anmerkung fehlt.

Der Grund dafür ist die Tatsache, dass ich zu Unrecht die Namensräume wurde mit:

Das Plugin

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

Also die XSD haben müssen diese einen bestimmten Namespace konfiguriert ist als Ziel-Namensraum (zB):

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

Mit freundlichen Grüßen Antonio

Verwandte Themen