春季在使用MethodValidationPostProcessor时返回404。

huangapple go评论125阅读模式
英文:

Spring returns 404 when using MethodValidationPostProcessor

问题

以下是您提供的内容的翻译:

我在测试Spring Boot应用程序时遇到了问题。它正常运行,但当我通过添加依赖项等启用Spring验证并添加@Configuration时:

@Configuration
public class TestConfiguration {

  @Bean
  public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
  }
}

我在测试端点上收到404错误。

{
    "timestamp": 1601507037178,
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/test"
}

我已经尝试了一些类似问题的解决方案/建议(例如这里这里),但都没有成功。

这是我的代码:
https://github.com/zolv/error-handling-test

API接口:

@Validated
public interface TestApi {

  @PostMapping(
      value = "/test",
      produces = {"application/json"},
      consumes = {"application/json"})
  @ResponseBody
  ResponseEntity<TestEntity> getTest(@Valid @RequestBody(required = false) TestEntity request);
}

TestEntity 只是为了发送一些内容:

@Data
public class TestEntity {
  @JsonProperty("test")
  @NotNull
  private String test;
}

控制器实现:

@RestController
@RequiredArgsConstructor
@Validated
public class TestController implements TestApi {
  @Override
  @ResponseBody
  public ResponseEntity<TestEntity> getTest(@Valid @RequestBody TestEntity request) {
    return ResponseEntity.ok(request);
  }
}

我的控制器建议:

@ControllerAdvice
public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {

  @ExceptionHandler(value = {ConstraintViolationException.class})
  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ResponseBody
  public ResponseEntity<String> handleValidationFailure(ConstraintViolationException ex) {
    StringBuilder messages = new StringBuilder();

    for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
      messages.append(violation.getMessage());
    }

    return ResponseEntity.badRequest().body(messages.toString());
  }

  @Override
  @ResponseBody
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  protected ResponseEntity<Object> handleMethodArgumentNotValid(
      MethodArgumentNotValidException ex,
      HttpHeaders headers,
      HttpStatus status,
      WebRequest request) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST)
        .contentType(MediaType.APPLICATION_PROBLEM_JSON)
        .body("problem");
  }
}

应用程序:

@SpringBootApplication
@EnableWebMvc
@ComponentScan(basePackages = "com.test")
public class TestApplication {

  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }
}

我使用的测试,但在使用Postman时也失败了:

@SpringJUnitConfig
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TestControllerTest {

  @Autowired protected TestRestTemplate restTemplate;

  @Test
  void registrationHappyPath() throws Exception {
    /*
     * Given
     */
    final TestEntity request = new TestEntity();

    /*
     * When/
     */
    final ResponseEntity<String> response =
        restTemplate.postForEntity("/test", request, String.class);

    /*
     * Then
     */
    Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());

    final String body = response.getBody();
    Assertions.assertNotNull(body);
  }
}

如果我注释掉TestConfiguration,那么一切都正常工作。
非常感谢您提前的任何帮助。

英文:

I have a problem with my test Spring Boot app. It works just fine, but when I enable Spring validation by adding dependency etc and adding a @Configuration:

@Configuration
public class TestConfiguration {

  @Bean
  public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
  }
}

I get 404 for my test endpoint.

{
    &quot;timestamp&quot;: 1601507037178,
    &quot;status&quot;: 404,
    &quot;error&quot;: &quot;Not Found&quot;,
    &quot;message&quot;: &quot;No message available&quot;,
    &quot;path&quot;: &quot;/test&quot;
}

I've already applied some solutions/proposals from similar problems (eg here or here) but without a success.

Here is my code:
https://github.com/zolv/error-handling-test

API interface:

@Validated
public interface TestApi {

  @PostMapping(
      value = &quot;/test&quot;,
      produces = {&quot;application/json&quot;},
      consumes = {&quot;application/json&quot;})
  @ResponseBody
  ResponseEntity&lt;TestEntity&gt; getTest(@Valid @RequestBody(required = false) TestEntity request);
}

TestEntity just to send something:

@Data
public class TestEntity {
  @JsonProperty(&quot;test&quot;)
  @NotNull
  private String test;
}

Controller implementation:

@RestController
@RequiredArgsConstructor
@Validated
public class TestController implements TestApi {
  @Override
  @ResponseBody
  public ResponseEntity&lt;TestEntity&gt; getTest(@Valid @RequestBody TestEntity request) {
    return ResponseEntity.ok(request);
  }
}

My controller advice:

@ControllerAdvice
public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {

  @ExceptionHandler(value = {ConstraintViolationException.class})
  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ResponseBody
  public ResponseEntity&lt;String&gt; handleValidationFailure(ConstraintViolationException ex) {
    StringBuilder messages = new StringBuilder();

    for (ConstraintViolation&lt;?&gt; violation : ex.getConstraintViolations()) {
      messages.append(violation.getMessage());
    }

    return ResponseEntity.badRequest().body(messages.toString());
  }

  @Override
  @ResponseBody
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  protected ResponseEntity&lt;Object&gt; handleMethodArgumentNotValid(
      MethodArgumentNotValidException ex,
      HttpHeaders headers,
      HttpStatus status,
      WebRequest request) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST)
        .contentType(MediaType.APPLICATION_PROBLEM_JSON)
        .body(&quot;problem&quot;);
  }
}

Application:

@SpringBootApplication
@EnableWebMvc
@ComponentScan(basePackages = &quot;com.test&quot;)
public class TestApplication {

  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }
}

A test I use, but it fails also using Postman:

@SpringJUnitConfig
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TestControllerTest {

  @Autowired protected TestRestTemplate restTemplate;

  @Test
  void registrationHappyPath() throws Exception {
    /*
     * Given
     */
    final TestEntity request = new TestEntity();

    /*
     * When/
     */
    final ResponseEntity&lt;String&gt; response =
        restTemplate.postForEntity(&quot;/test&quot;, request, String.class);

    /*
     * Then
     */
    Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());

    final String body = response.getBody();
    Assertions.assertNotNull(body);
  }
}

If I comment out a TestConfiguration then everything works fine.
Thank You in advance for any help.

答案1

得分: 4

你应该设置 MethodValidationPostProcessor#setProxyTargetClass(true),因为默认情况下 MethodValidationPostProcessor 使用 JDK 代理,这会导致在 Spring 上下文中丢失你的控制器。

当调用 AbstractHandlerMethodMapping#processCandidateBean 时,isHandler(Class&lt;?&gt; beanType) 将返回 false,因为 JDK 代理不包含 @RestController 注解。

  public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
    mvProcessor.setProxyTargetClass(true);
    return mvProcessor;
  }
英文:

You should set MethodValidationPostProcessor#setProxyTargetClass(true) because by default MethodValidationPostProcessor uses JDK proxy which leads to loss of your controller in the Spring context.

When AbstractHandlerMethodMapping#processCandidateBean is called isHandler(Class&lt;?&gt; beanType) will return false because JDK proxy doesn't contain @RestController annotation.

  public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
    mvProcessor.setProxyTargetClass(true);
    return mvProcessor;
  }

huangapple
  • 本文由 发表于 2020年10月1日 07:20:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/64147036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定