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
pull/3948/merge
Stephane Nicoll 9 years ago
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…
Cancel
Save