Extract BindingResult if necessary
Previously, no `errors` attribute is made available in the standard JSON error document if a request body object is invalid. This is due to the fact that the framework throws a `MethodArgumentNotValidException holding a `BindingResult` object that was not detected. We now make sure to extract the `BindingResult` from such exception. Closes gh-4166
This commit is contained in:
parent
17fde264e2
commit
266335339d
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -32,6 +32,7 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
|
@ -52,6 +53,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.1.0
|
||||
* @see ErrorAttributes
|
||||
*/
|
||||
|
|
@ -130,11 +132,11 @@ public class DefaultErrorAttributes
|
|||
}
|
||||
|
||||
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
|
||||
if (!(error instanceof BindingResult)) {
|
||||
BindingResult result = extractBindingResult(error);
|
||||
if (result == null) {
|
||||
errorAttributes.put("message", error.getMessage());
|
||||
return;
|
||||
}
|
||||
BindingResult result = (BindingResult) error;
|
||||
if (result.getErrorCount() > 0) {
|
||||
errorAttributes.put("errors", result.getAllErrors());
|
||||
errorAttributes.put("message",
|
||||
|
|
@ -146,6 +148,16 @@ public class DefaultErrorAttributes
|
|||
}
|
||||
}
|
||||
|
||||
private BindingResult extractBindingResult(Throwable error) {
|
||||
if (error instanceof BindingResult) {
|
||||
return (BindingResult) error;
|
||||
}
|
||||
if (error instanceof MethodArgumentNotValidException) {
|
||||
return ((MethodArgumentNotValidException) error).getBindingResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
|
||||
StringWriter stackTrace = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(stackTrace));
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import java.util.Map;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
|
@ -45,7 +47,9 @@ import org.springframework.test.annotation.DirtiesContext;
|
|||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
|
@ -164,6 +168,20 @@ public class BasicErrorControllerIntegrationTests {
|
|||
assertThat(resp, containsString("org.springframework.validation.BindException"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testRequestBodyValidationForMachineClient() throws Exception {
|
||||
load();
|
||||
RequestEntity request = RequestEntity.post(URI.create(createUrl("/bodyValidation")))
|
||||
.contentType(MediaType.APPLICATION_JSON).body("{}");
|
||||
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
||||
String resp = entity.getBody().toString();
|
||||
assertThat(resp, containsString("Error count: 1"));
|
||||
assertThat(resp, containsString("errors=[{"));
|
||||
assertThat(resp, containsString("codes=["));
|
||||
assertThat(resp, containsString("org.springframework.web.bind.MethodArgumentNotValidException"));
|
||||
}
|
||||
|
||||
private void assertErrorAttributes(Map<?, ?> content, String status, String error,
|
||||
Class<?> exception, String message, String path) {
|
||||
assertEquals("Wrong status", status, content.get("status"));
|
||||
|
|
@ -239,6 +257,11 @@ public class BasicErrorControllerIntegrationTests {
|
|||
throw error;
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/bodyValidation", method = RequestMethod.POST, produces = "application/json")
|
||||
public String bodyValidation(@Valid @RequestBody DummyBody body) {
|
||||
return body.content;
|
||||
}
|
||||
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Expected!")
|
||||
@SuppressWarnings("serial")
|
||||
private static class ExpectedException extends RuntimeException {
|
||||
|
|
@ -255,6 +278,20 @@ public class BasicErrorControllerIntegrationTests {
|
|||
|
||||
}
|
||||
|
||||
private static class DummyBody {
|
||||
|
||||
@NotNull
|
||||
private String content;
|
||||
|
||||
public String getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -29,6 +29,7 @@ import org.springframework.validation.BindException;
|
|||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.MapBindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
|
@ -162,7 +163,20 @@ public class DefaultErrorAttributesTests {
|
|||
BindingResult bindingResult = new MapBindingResult(
|
||||
Collections.singletonMap("a", "b"), "objectName");
|
||||
bindingResult.addError(new ObjectError("c", "d"));
|
||||
BindException ex = new BindException(bindingResult);
|
||||
Exception ex = new BindException(bindingResult);
|
||||
testBindingResult(bindingResult, ex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractMethodArgumentNotValidExceptionBindingResultErrors() throws Exception {
|
||||
BindingResult bindingResult = new MapBindingResult(
|
||||
Collections.singletonMap("a", "b"), "objectName");
|
||||
bindingResult.addError(new ObjectError("c", "d"));
|
||||
Exception ex = new MethodArgumentNotValidException(null, bindingResult);
|
||||
testBindingResult(bindingResult, ex);
|
||||
}
|
||||
|
||||
private void testBindingResult(BindingResult bindingResult, Exception ex) {
|
||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||
Map<String, Object> attributes = this.errorAttributes
|
||||
.getErrorAttributes(this.requestAttributes, false);
|
||||
|
|
|
|||
Loading…
Reference in New Issue