2013-05-20 40 views
10

當在REST控制器中使用@ControllerAdvice@Valid註解時,我遇到了問題。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錯誤請求響應。 (對於無效請求我的意思是在未指定用戶名是一個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註釋。

context.xml中是很容易的,只包含了控制器掃描儀,並使MVC:註解驅動

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

有誰知道如何在工作和@ControllerAdvice註釋@Valid一起REST控制器?

在此先感謝。 Antonio

回答

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

MethodArgumentNotValidException的JavaDoc:

異常時與@Valid註釋參數驗證失敗被拋出。

換句話說,當驗證失敗時會拋出MethodArgumentNotValidException。它由ResponseEntityExceptionHandler提供的handleMethodArgumentNotValid()方法處理,如果您想要定製實現,則需要覆蓋該方法。

+0

謝謝,這正是我一直在尋找的。 –

+1

@matsev代替擴展ResponseEntityExceptionHandler並重寫handleMethodArgumentNotValid方法,他不能簡單地使用@ExceptionHandler(MethodArgumentNotValidException.class)嗎? – Stephane

+0

@StephaneEybert當然,爲什麼不呢。我選擇擴展'ResponseEntityExceptionHandler',因爲它已經在原始問題中擴展了。相關:通過使用'@ ControllerAdvice'註釋(無論是擴展'ResponseEntityExceptionHandler'還是使用'@ ExceptionHandler'註釋),您都可以對所有控制器進行一致的錯誤處理。 – matsev

1

除此之外,我還有另一個遞歸驗證問題。

生成的類在其他XmlElements上缺少@valid註釋。

這樣做的原因是有關我被錯誤地使用的命名空間的事實:

插件配置爲使用特定的命名空間

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

親切的問候 安東尼奧