0

我发现在 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)
        ...

我怎样才能防止这种情况?

处理程序和端点之间是否有一些拦截器?

4

0 回答 0