java Spring Rest ErrorHandling @ControllerAdvice / @Valid

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16651160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 23:34:39  来源:igfitidea点击:

Spring Rest ErrorHandling @ControllerAdvice / @Valid

javaspringresterror-handling

提问by daemon_nio

I'm having trouble when Using @ControllerAdviceand @Validannotations together in a REST controller.

在 REST 控制器中一起使用@ControllerAdvice@Valid注释时遇到问题。

I have a rest controller declared as follows:

我有一个rest控制器声明如下:

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

And an ErrorHandler class:

还有一个 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);
    }
}

The problem is that, if I add a "throw new RuntimeException"within the method RestExample.addthen the exception is correctly handled by RestErrorHandlerclass.

问题是,如果我在方法RestExample.add 中添加“throw new RuntimeException”,那么RestErrorHandler类会正确处理异常。

However when curling a not valid request to the controller the RestErrorHandlerdoesn't catch the exception thrown by the validator and I receive a 400 BadRequestresponse. (For not valid request I mean an xml request where Username is not specified)

但是,当向控制器卷曲无效请求时,RestErrorHandler不会捕获验证器抛出的异常,并且我收到400 BadRequest响应。(对于无效请求,我的意思是未指定用户名的 xml 请求)

Note that XmlRequestUserclass is autogenerated by the plugin maven-jaxb2-plugin+ krasa-jaxb-tools(pom.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>

The generated class has correctly a @NotNull Annotation on Username and Password fields.

生成的类在用户名和密码字段上正确地具有 @NotNull 注释。

My context.xmlis very easy, containing just a scanner for controllers and enabling mvc:annotation-driven

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

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

Does anybody know how to make working @ControllerAdvice and @Valid annotations together in a REST controller?

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

Thanks in advance. Antonio

提前致谢。安东尼奥

回答by matsev

You are on the right track, but you need to override the handleMethodArgumentNotValid()instead of the handleException()method, e.g.

您在正确的轨道上,但您需要覆盖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);
    }
}

From the JavaDoc of MethodArgumentNotValidException:

来自MethodArgumentNotValidException的 JavaDoc :

Exception to be thrown when validation on an argument annotated with @Valid fails.

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

In other words, a MethodArgumentNotValidExceptionis thrown when the validation fails. It is handled by the handleMethodArgumentNotValid()method provided by the ResponseEntityExceptionHandler, that needs to be overridden if you would like a custom implementation.

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

回答by daemon_nio

In addition to this I had another problem on recursive validation.

除此之外,我在递归验证方面还有另一个问题。

The generated classes were missing a @validannotation on the other XmlElements.

生成的类在其他 XmlElements 上缺少@valid注释。

The reason for this is related to the fact that I was wrongly using the namespaces:

这样做的原因与我错误地使用命名空间有关:

The plugin is configured to use a particular namespace

该插件被配置为使用特定的命名空间

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

So the XSD need to have this as target namespace (e.g.):

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

Kind regards Antonio

亲切的问候安东尼奥

回答by Th??ng ??c

Better format of return message:

更好的返回消息格式:

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

}