【问题标题】:Spring MVC Rest Controller @RequestBody ParsingSpring MVC Rest Controller @RequestBody 解析
【发布时间】:2016-02-10 17:49:07
【问题描述】:

我有一个 spring rest web 应用程序,其中包含一个通用的 rest 控制器,如下所示。让杰克逊将对象序列化为 JSON,GET 方法运行良好。但是,当我尝试调用 save 方法时,RequestBody 参数被转换为 LinkedHashMap 而不是 T 泛型类型定义的类型。

@RestController
public abstract class CrudAPI<T extends Object, ID extends Serializable>{

    @Transactional
    @RequestMapping(method = RequestMethod.POST, consumes = "application/json")
    public ResponseEntity<Void> save(@RequestBody T entity){
        service.save(entity);
        return new ResponseEntity(HttpStatus.CREATED);
    }

}

JSON:

{
   "id":null,
   "version":null,
   "name":"Copel",
   "disabled":false,
   "type":"P",
   "labels":[
      {
         "id":null,
         "version":null,
         "name":"unidade consumidora"
      }
   ]
}

我收到以下错误:

HTTP 状态 500 - 请求处理失败;嵌套异常是 org.springframework.beans.NotReadablePropertyException:无效 bean 类 [java.util.LinkedHashMap] 的属性“版本”:不能 在回退访问期间查找属性字段!

Spring 配置:

@Configuration
@Import(Application.class)
@EnableWebMvc
@ComponentScan(basePackages = {"br.com.doc2cloud"})
public class WebConfig extends WebMvcConfigurerAdapter implements WebApplicationInitializer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    private MappingJackson2HttpMessageConverter jacksonConverter() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate5Module());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(new ISO8601DateFormat());
        mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
        jacksonConverter.setObjectMapper(mapper);

        return jacksonConverter;
    }

 @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jacksonConverter());
        super.configureMessageConverters(converters);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setInitParameter("javax.servlet.jsp.jstl.fmt.localizationContext", "messages");
        EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("characterEncoding", characterEncodingFilter);
        characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
    }
}

我的代码有什么问题?

【问题讨论】:

  • 风格不好。控制器永远不应该是@Transactional!
  • 感谢您的建议!

标签: java json spring rest spring-mvc


【解决方案1】:

我不认为你可以实现你想要的。 Java 泛型机制仅在编译时起作用。编译后泛型类型被删除并被实际类型替换(在您的情况下为Object)。您的控制器无法理解您尝试解析 JSON 数据的类型。

您得到LinkedHashMap 的原因是Jackson 默认将它用于“未知”类型。典型的 JSON 数据实际上是一个键值映射,Jackson 保留了属性排序 - 这就是使用 linked 哈希映射的原因。

【讨论】:

  • 这并不完全正确。泛型对象实例的泛型被擦除(例如,您找不到 ArrayList 实例的泛型类型)。但是,如果您有一个扩展泛型类的类(我假设这篇文章的作者正在这样做,因为他的泛型类是抽象的),那么在运行时就知道该类的泛型类型是什么。这就是为什么像 Guava 的 TypeToken 这样的类能够确定关于类的泛型类型的信息。
【解决方案2】:

您使用的是哪个版本的 Jackson?我升级到 2.7.3 并且在使用泛型时(我有一个具有用于保存、列出等通用逻辑的基本控制器)我遇到了同样的问题。回滚到 2.6.5 允许我继续使用我的通用基类。我还没有研究问题的原因,但回滚为我修复了它。

【讨论】:

  • spring 4.3 发布时应该会解决这个问题:完全支持 Jackson 2.7 的新类型解析算法
猜你喜欢
  • 2017-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 2020-05-30
相关资源
最近更新 更多