英文:
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.
{
"timestamp": 1601507037178,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/test"
}
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 = "/test",
produces = {"application/json"},
consumes = {"application/json"})
@ResponseBody
ResponseEntity<TestEntity> getTest(@Valid @RequestBody(required = false) TestEntity request);
}
TestEntity
just to send something:
@Data
public class TestEntity {
@JsonProperty("test")
@NotNull
private String test;
}
Controller implementation:
@RestController
@RequiredArgsConstructor
@Validated
public class TestController implements TestApi {
@Override
@ResponseBody
public ResponseEntity<TestEntity> 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<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");
}
}
Application:
@SpringBootApplication
@EnableWebMvc
@ComponentScan(basePackages = "com.test")
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<String> response =
restTemplate.postForEntity("/test", 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<?> 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<?> beanType)
will return false
because JDK proxy doesn't contain @RestController
annotation.
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
mvProcessor.setProxyTargetClass(true);
return mvProcessor;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论