【问题标题】:Spring Rest ErrorHandling @ControllerAdvice / @ValidSpring Rest ErrorHandling @ControllerAdvice / @Valid
【发布时间】:2013-05-15 02:35:24
【问题描述】:

在 REST 控制器中同时使用 @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);
    }
}

问题是,如果我在 RestExample.add 方法中添加一个 "throw new RuntimeException",那么 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 注释。

我的 context.xml 非常简单,只包含一个控制器扫描器并启用 mvc:annotation-driven

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

有人知道如何在 REST 控制器中同时使用 @ControllerAdvice 和 @Valid 注释吗?

提前致谢。 安东尼奥

【问题讨论】:

    标签: java spring rest error-handling


    【解决方案1】:

    您在正确的轨道上,但您需要覆盖 handleMethodArgumentNotValid() 而不是 handleException() 方法,例如

    @ControllerAdvice
    public class RestErrorHandler extends ResponseEntityExceptionHandler {
    
        @Override
        protected ResponseEntity<Object> handleMethodArgumentNotValid(
                MethodArgumentNotValidException exception,
                HttpHeaders headers,
                HttpStatus status,
                WebRequest request) {
    
            LOG.error(exception);
            String bodyOfResponse = exception.getMessage();
            return new ResponseEntity(errorMessage, headers, status);
        }
    }
    

    来自MethodArgumentNotValidException的JavaDoc:

    当对带有@Valid 注释的参数的验证失败时抛出异常。

    换句话说,当验证失败时会抛出MethodArgumentNotValidException。它由ResponseEntityExceptionHandler 提供的handleMethodArgumentNotValid() 方法处理,如果您想要自定义实现,则需要覆盖该方法。

    【讨论】:

    • 谢谢,这正是我想要的。
    • @matsev 不是扩展 ResponseEntityExceptionHandler 并覆盖 handleMethodArgumentNotValid 方法,他不能简单地使用 @ExceptionHandler(MethodArgumentNotValidException.class) 吗?
    • @StephaneEybert 当然,为什么不呢。我选择扩展ResponseEntityExceptionHandler,因为它已经在原始问题中扩展。相关:通过使用@ControllerAdvice 注释(无论是扩展ResponseEntityExceptionHandler 还是使用@ExceptionHandler 注释),您都可以对所有控制器进行一致的错误处理。
    • 我已经重写了方法handleMethodArgumentNotValid,但这似乎没有使用,我通过设置断点检查了调试。除了覆盖该方法之外,我还需要做其他配置吗?
    • 我在@controllerAdvice 中覆盖了handleMethodArgumentNotValid() 但当所需参数不存在时我无法处理异常
    【解决方案2】:

    更好的返回信息格式:

    @ControllerAdvice
    public class MyControllerAdvice extends ResponseEntityExceptionHandler {
    
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers,
            HttpStatus status,
            WebRequest request) {
        BindingResult result = ex.getBindingResult();
        List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
                .map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode()))
                .collect(Collectors.toList());
    
        return handleExceptionInternal(ex, "Method argument not valid, fieldErrors:" + fieldErrors ,new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      除此之外,我在递归验证方面遇到了另一个问题。

      生成的类在其他 XmlElement 上缺少 @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>
      

      亲切的问候 安东尼奥

      【讨论】:

        猜你喜欢
        • 2020-06-05
        • 1970-01-01
        • 1970-01-01
        • 2022-01-17
        • 2019-08-12
        • 2023-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多