我发现在 Spring Boot 2.0.3 中,传递给异常处理程序并返回到前端的 JSON 错误消息将被格式化为字符串而不是 JSON,并\
在每个双引号之前转义(即,{"foo":"bar"}
将是"{\"foo\":\"bar\"}"
.
具体来说,我有一个将 ajava.util.Map
转换为 JSON 字符串的方法。方法是:
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class Utilities {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static String jsonBuilder(Map<String, Object> resultMap) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
for (String key: resultMap.keySet()) {
Object value = resultMap.get(key);
if (value instanceof String) {
node.put(key, (String)value);
} else if (value instanceof Set) {
ArrayNode arrayNode = mapper.createArrayNode();
((Set) value).forEach(e -> arrayNode.add(e.toString()));
node.set(key, arrayNode); //put() is deprecated
}
}
return mapper.writeValueAsString(node);
}
}
而这个测试:
import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
public class UtilitiesTests {
@Test
public void testJsonBuilder() throws JsonProcessingException {
Map<String, Object> map = new HashMap<String, Object>();
map.put("key1", "value1");
String result = Utilities.jsonBuilder(map);
assertThat(result).isEqualTo("{\"key1\":\"value1\"}");
Map<String, Object> map1 = new HashMap<String, Object>();
Set<String> set = new LinkedHashSet<String>(); //we must retain order.
set.add("arrVal1");
set.add("arrVal2");
map1.put("key2", "value2");
map1.put("key3", set);
String result1 = Utilities.jsonBuilder(map1);
assertThat(result1).isEqualTo("{\"key2\":\"value2\",\"key3\":[\"arrVal1\",\"arrVal2\"]}");
}
}
测试总是通过,即该方法运行良好并且可以将地图转换为正确的 JSON。
现在,如果我直接从某个@RequestMapping
方法返回到 RESTful 端点(从 Postman 测试),则返回的值是 JSON,没有任何引号。
就像是:
List<BinInfo> founds = repository.findAllByBin(bin);
BodyBuilder builder = ResponseEntity.status(HttpStatus.OK);
builder.contentType(MediaType.APPLICATION_JSON_UTF8);
if (founds != null && !founds.isEmpty() && founds.size() == 1) {
return builder.body(founds.get(0));
} else {
errors.put("error", "BIN not found");
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(Utilities.jsonBuilder(errors));
}
将返回:
{
"error": "BIN not found"
}
但是,如果我将格式错误的参数传递给该方法,org.springframework.web.bind.MethodArgumentNotValidException
则会发生异常并被此异常处理程序捕获:
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, WebRequest request) throws JsonProcessingException {
BindingResult result = ex.getBindingResult();
final List<FieldError> fieldErrors = result.getFieldErrors();
final Set<String> errors = new HashSet<>();
for (FieldError fe: fieldErrors) {
errors.add(fe.getField());
}
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("error", "validation");
resultMap.put("fields", errors);
log.error("Validation error. ", ex);
return ResponseEntity.badRequest()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(Utilities.jsonBuilder(resultMap));
}
而且,返回的不是 JSON,而是带引号和转义的字符串,例如:
"{\"error\":\"validation\"}"
我知道异常被捕获,因为我在日志中看到了这些:
[NODE=xxxxxx] [ENV=dev] [SRC=UNDEFINED] [TRACE=] [SPAN=] [2018-08-21T12:43:50.722Z] [ERROR] [MSG=[XNIO-2 task-2] c.p.b.controller.BinInfoController - Validation error. org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument at index 0 in method: public org.springframework.http.ResponseEntity<java.lang.Object> com.xxxxx.binlookup.controller.BinInfoController.insertBIN(com.xxxxxx.binlookup.model.BinInfo) throws com.fasterxml.jackson.core.JsonProcessingException, with 1 error(s): [Field error in object 'binInfo' on field 'bin': rejected value [11]; codes [Size.binInfo.bin,Size.bin,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [binInfo.bin,bin]; arguments []; default message [bin],8,6]; default message [?????6?8??]]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
...
我怎样才能防止这种情况?
处理程序和端点之间是否有一些拦截器?