春季在使用MethodValidationPostProcessor时返回404。

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

Spring returns 404 when using MethodValidationPostProcessor

问题

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

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

  1. @Configuration
  2. public class TestConfiguration {
  3. @Bean
  4. public MethodValidationPostProcessor methodValidationPostProcessor() {
  5. return new MethodValidationPostProcessor();
  6. }
  7. }

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

  1. {
  2. "timestamp": 1601507037178,
  3. "status": 404,
  4. "error": "Not Found",
  5. "message": "No message available",
  6. "path": "/test"
  7. }

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

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

API接口:

  1. @Validated
  2. public interface TestApi {
  3. @PostMapping(
  4. value = "/test",
  5. produces = {"application/json"},
  6. consumes = {"application/json"})
  7. @ResponseBody
  8. ResponseEntity<TestEntity> getTest(@Valid @RequestBody(required = false) TestEntity request);
  9. }

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

  1. @Data
  2. public class TestEntity {
  3. @JsonProperty("test")
  4. @NotNull
  5. private String test;
  6. }

控制器实现:

  1. @RestController
  2. @RequiredArgsConstructor
  3. @Validated
  4. public class TestController implements TestApi {
  5. @Override
  6. @ResponseBody
  7. public ResponseEntity<TestEntity> getTest(@Valid @RequestBody TestEntity request) {
  8. return ResponseEntity.ok(request);
  9. }
  10. }

我的控制器建议:

  1. @ControllerAdvice
  2. public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {
  3. @ExceptionHandler(value = {ConstraintViolationException.class})
  4. @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  5. @ResponseBody
  6. public ResponseEntity<String> handleValidationFailure(ConstraintViolationException ex) {
  7. StringBuilder messages = new StringBuilder();
  8. for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
  9. messages.append(violation.getMessage());
  10. }
  11. return ResponseEntity.badRequest().body(messages.toString());
  12. }
  13. @Override
  14. @ResponseBody
  15. @ResponseStatus(HttpStatus.BAD_REQUEST)
  16. protected ResponseEntity<Object> handleMethodArgumentNotValid(
  17. MethodArgumentNotValidException ex,
  18. HttpHeaders headers,
  19. HttpStatus status,
  20. WebRequest request) {
  21. return ResponseEntity.status(HttpStatus.BAD_REQUEST)
  22. .contentType(MediaType.APPLICATION_PROBLEM_JSON)
  23. .body("problem");
  24. }
  25. }

应用程序:

  1. @SpringBootApplication
  2. @EnableWebMvc
  3. @ComponentScan(basePackages = "com.test")
  4. public class TestApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(TestApplication.class, args);
  7. }
  8. }

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

  1. @SpringJUnitConfig
  2. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  3. class TestControllerTest {
  4. @Autowired protected TestRestTemplate restTemplate;
  5. @Test
  6. void registrationHappyPath() throws Exception {
  7. /*
  8. * Given
  9. */
  10. final TestEntity request = new TestEntity();
  11. /*
  12. * When/
  13. */
  14. final ResponseEntity<String> response =
  15. restTemplate.postForEntity("/test", request, String.class);
  16. /*
  17. * Then
  18. */
  19. Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
  20. final String body = response.getBody();
  21. Assertions.assertNotNull(body);
  22. }
  23. }

如果我注释掉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:

  1. @Configuration
  2. public class TestConfiguration {
  3. @Bean
  4. public MethodValidationPostProcessor methodValidationPostProcessor() {
  5. return new MethodValidationPostProcessor();
  6. }
  7. }

I get 404 for my test endpoint.

  1. {
  2. &quot;timestamp&quot;: 1601507037178,
  3. &quot;status&quot;: 404,
  4. &quot;error&quot;: &quot;Not Found&quot;,
  5. &quot;message&quot;: &quot;No message available&quot;,
  6. &quot;path&quot;: &quot;/test&quot;
  7. }

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:

  1. @Validated
  2. public interface TestApi {
  3. @PostMapping(
  4. value = &quot;/test&quot;,
  5. produces = {&quot;application/json&quot;},
  6. consumes = {&quot;application/json&quot;})
  7. @ResponseBody
  8. ResponseEntity&lt;TestEntity&gt; getTest(@Valid @RequestBody(required = false) TestEntity request);
  9. }

TestEntity just to send something:

  1. @Data
  2. public class TestEntity {
  3. @JsonProperty(&quot;test&quot;)
  4. @NotNull
  5. private String test;
  6. }

Controller implementation:

  1. @RestController
  2. @RequiredArgsConstructor
  3. @Validated
  4. public class TestController implements TestApi {
  5. @Override
  6. @ResponseBody
  7. public ResponseEntity&lt;TestEntity&gt; getTest(@Valid @RequestBody TestEntity request) {
  8. return ResponseEntity.ok(request);
  9. }
  10. }

My controller advice:

  1. @ControllerAdvice
  2. public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {
  3. @ExceptionHandler(value = {ConstraintViolationException.class})
  4. @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  5. @ResponseBody
  6. public ResponseEntity&lt;String&gt; handleValidationFailure(ConstraintViolationException ex) {
  7. StringBuilder messages = new StringBuilder();
  8. for (ConstraintViolation&lt;?&gt; violation : ex.getConstraintViolations()) {
  9. messages.append(violation.getMessage());
  10. }
  11. return ResponseEntity.badRequest().body(messages.toString());
  12. }
  13. @Override
  14. @ResponseBody
  15. @ResponseStatus(HttpStatus.BAD_REQUEST)
  16. protected ResponseEntity&lt;Object&gt; handleMethodArgumentNotValid(
  17. MethodArgumentNotValidException ex,
  18. HttpHeaders headers,
  19. HttpStatus status,
  20. WebRequest request) {
  21. return ResponseEntity.status(HttpStatus.BAD_REQUEST)
  22. .contentType(MediaType.APPLICATION_PROBLEM_JSON)
  23. .body(&quot;problem&quot;);
  24. }
  25. }

Application:

  1. @SpringBootApplication
  2. @EnableWebMvc
  3. @ComponentScan(basePackages = &quot;com.test&quot;)
  4. public class TestApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(TestApplication.class, args);
  7. }
  8. }

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

  1. @SpringJUnitConfig
  2. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  3. class TestControllerTest {
  4. @Autowired protected TestRestTemplate restTemplate;
  5. @Test
  6. void registrationHappyPath() throws Exception {
  7. /*
  8. * Given
  9. */
  10. final TestEntity request = new TestEntity();
  11. /*
  12. * When/
  13. */
  14. final ResponseEntity&lt;String&gt; response =
  15. restTemplate.postForEntity(&quot;/test&quot;, request, String.class);
  16. /*
  17. * Then
  18. */
  19. Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
  20. final String body = response.getBody();
  21. Assertions.assertNotNull(body);
  22. }
  23. }

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 注解。

  1. public MethodValidationPostProcessor methodValidationPostProcessor() {
  2. MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
  3. mvProcessor.setProxyTargetClass(true);
  4. return mvProcessor;
  5. }
英文:

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.

  1. public MethodValidationPostProcessor methodValidationPostProcessor() {
  2. MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
  3. mvProcessor.setProxyTargetClass(true);
  4. return mvProcessor;
  5. }

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:

确定