作为ResquestBody JSON一部分的重复键或额外键值是否会导致REST环境中的错误请求?

作为ResquestBody JSON一部分的重复键或额外键值是否会导致REST环境中的错误请求?,json,spring,rest,spring-mvc,Json,Spring,Rest,Spring Mvc,我不熟悉REST环境,我有一个非常简单的bean,名为Operations,具有以下属性 public class Operations { String operationName, int operationPriority, boolean isOperationParallel, boolean expirationPolicy } 因此,要创建一个操作bean,请求JSON将是 '{ "operationName": "NewOperation", "operationPri

我不熟悉REST环境,我有一个非常简单的bean,名为Operations,具有以下属性

public class Operations
{
String operationName,
int operationPriority,
boolean isOperationParallel,
boolean expirationPolicy
}
因此,要创建一个操作bean,请求JSON将是

'{
  "operationName": "NewOperation",
  "operationPriority": 2,
  "isOperationParallel": false,
  "expirationPolicy": 30
}'
在服务器端,为了将请求JSON映射到操作bean,我使用了Spring ResquestBody,默认情况下,给定重复的键,它会用最后一个键值覆盖,例如,如果下面是请求,
operationName
将是
NewOperationTwo

'{
      "operationName": "NewOperation",
      "operationName": "NewOperationTwo",
      "operationPriority": 2,
      "isOperationParallel": false,
      "expirationPolicy": 30
    }'
现在我们的QA告诉我,作为HTTP状态响应,它应该导致
400 BAD_请求
。我还被告知,如果像“Dog”这样的未知属性,“Barks”也作为请求体的一部分传递,那么它也应该导致400响应,Jackson默认情况下也会忽略

现在我想说的是,我可以通过解析请求并找到重复的和未知的属性来发送
400
,但这需要开销吗?因为如果它是一个命令,那么Spring在默认情况下会返回一个
BAD_请求
响应,而它不会


在这方面,有谁能帮我一个忙吗?对于请求体中的重复键和未知键,标准REST API响应是什么?如果我不将BAD_请求作为响应抛出,会有什么后果?

Json中没有为重复键和未知键定义标准REST API响应。但是,如果使用
内容类型
应用程序/x-www-form-urlencoded
mulitpart/from data
则请求参数名称应是唯一的。这不适用于JSON。因为无法确定每个应用程序中的json fromat是什么

因此,由组织的业务需求决定如何处理此类情况。软件开发人员的角色是在应用程序中实现这些约束

所以,如果你的QA工程师说400状态应该被发送回去进行复制 和未知字段。必须这样做

更新

如果JSON请求/响应结构是固定的。然后,您可以使用JAXB和设计模式对象(如值对象(VO))来处理常规POJO的传入和传出数据。 使用JAXB将Json绑定到java对象,然后可以使用框架提供的评估功能。这将大大减少你在没有它的情况下所付出的努力。如果您正在使用运动衫,请通过休闲链接了解更多信息


很抱歉对解决方案的响应太晚,但这就是我所说的

我为jackson创建了一个定制的HttpMessageConverter,最初我对MappingJackson2HttpMessageConverter和MappingJacksonHttpMessageConverter感到困惑 但请阅读Jackson和MappingJackson2HttpMessageConverter

下面是自定义消息转换器,基本上它需要覆盖以下三种方法

read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
                    throws IOException, HttpMessageNotReadableException 
read(类型、类contextClass、HttpInputMessage-inputMessage)
抛出IOException、HttpMessageNodeReadableException
是包含来自请求主体的消息并将其传递给ObjectMapper的方法

public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    private static final Logger logger =

    private ObjectMapper objectMapper;

    private boolean prefixJson = false;

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#setPrefixJson(boolean)
     */
    @Override
    public void setPrefixJson(boolean prefixJson) {
        this.prefixJson = prefixJson;
        super.setPrefixJson(prefixJson);
    }


    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#read(java.lang.reflect.Type,
     * java.lang.Class, org.springframework.http.HttpInputMessage)
     */
    @Override
    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
                    throws IOException, HttpMessageNotReadableException {
        objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true);
        objectMapper.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true);

        InputStream istream = inputMessage.getBody();
        String responseString = IOUtils.toString(istream);
        try {
            return objectMapper.readValue(responseString, OperatorTokenDefinition.class);
        } catch (UnrecognizedPropertyException ex) {
           throw new YourCustomExceptionClass();
        } catch (InvalidFormatException ex) { 
           throw new YourCustomExceptionClass();
        } catch (IgnoredPropertyException ex) {
            throw new YourCustomExceptionClass();
        } catch (JsonMappingException ex) {
            throw new YourCustomExceptionClass();
        } catch (JsonParseException ex) {
            logger.error("Could not read JSON JsonParseException:{}", ex);
            throw new YourCustomExceptionClass();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#supports(java.lang.Class)
     */
    @Override
    protected boolean supports(Class<?> arg0) {
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal(java.lang.Object,
     * org.springframework.http.HttpOutputMessage)
     */
    @Override
    protected void writeInternal(Object arg0, HttpOutputMessage outputMessage)
                    throws IOException, HttpMessageNotWritableException {
        objectMapper = new ObjectMapper();
        String json = this.objectMapper.writeValueAsString(arg0);
        outputMessage.getBody().write(json.getBytes(Charset.defaultCharset()));
    }

    /**
     * @return
     */
    private ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("messages");
        source.setUseCodeAsDefaultMessage(true);
        return source;
    }
}
公共类CustomMappingJackson2HttpMessageConverter扩展了MappingJackson2HttpMessageConverter{
专用静态最终记录器=
私有对象映射器对象映射器;
私有布尔前缀xjson=false;
/*
*(非Javadoc)
* 
*@see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#setPrefixJson(布尔值)
*/
@凌驾
public void setPrefixJson(布尔前缀JSON){
this.prefixJson=prefixJson;
super.setPrefixJson(prefixJson);
}
/*
*(非Javadoc)
* 
*@see org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#read(java.lang.reflect.Type,
*java.lang.Class,org.springframework.http.HttpInputMessage)
*/
@凌驾
公共对象读取(类型类型、类contextClass、HttpInputMessage inputMessage)
抛出IOException、HttpMessageNodeReadableException{
objectMapper=新的objectMapper();
configure(在未知属性上反序列化feature.FAIL,true);
configure(反序列化功能.ACCEPT_FLOAT_AS_INT,false);
configure(反序列化功能.FAIL_ON_NULL_对于_原语,为true);
configure(已忽略属性上的反序列化功能.FAIL\u,true);
configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION,true);
InputStream istream=inputMessage.getBody();
字符串responseString=IOUtils.toString(istream);
试一试{
返回objectMapper.readValue(responseString、OperatorTokenDefinition.class);
}捕获(无法识别的属性异常ex){
抛出新的YourCustomExceptionClass();
}catch(InvalidFormatException ex){
抛出新的YourCustomExceptionClass();
}捕获(IgnoredPropertyException ex){
抛出新的YourCustomExceptionClass();
}捕获(JsonMappingException ex){
抛出新的YourCustomExceptionClass();
}捕获(JsonParseException ex){
错误(“无法读取JSON JsonParseException:{}”,ex);
抛出新的YourCustomExceptionClass();
}
}
/*
*(非Javadoc)
* 
*@see org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#supports(java.lang.Class)
*/
@凌驾
受保护的布尔支持(类arg0){
返回true;
}
/*
*(非Javadoc)
* 
*@参见org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal(java.lang.Object,
*org.springframework.http.HttpOutputMessage)
*/
@凌驾
受保护的void writeInternal(对象arg0,
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    CustomMappingJackson2HttpMessageConverter jsonConverter =
                    CustomMappingJackson2HttpMessageConverter();
    List<MediaType> mediaTypeList = new ArrayList<MediaType>();
    mediaTypeList.add(MediaType.APPLICATION_JSON);
    jsonConverter.setSupportedMediaTypes(mediaTypeList);
    converters.add(jsonConverter);
    super.configureMessageConverters(converters);
}