2013-05-20 41 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> 

亲切的问候 安东尼奥