【问题标题】:Customize Spring @RequestParam Deserialization for Maps and/or Nested Objects为地图和/或嵌套对象自定义 Spring @RequestParam 反序列化
【发布时间】:2015-02-05 16:46:55
【问题描述】:
@RestController
class MyController {
     @RequestMapping(...)
     public void test(Container container) { ... }
}

Spring 默认使用 Dot-Notation 反序列化嵌套的 @RequestParam:

class Container {
    A a;
}

class A {
    String val;
}

适用于:

http://.../myController?a.val=foo

但对于地图,它使用方括号表示法:

class Container {
    Map<String, String> a;
}

适用于:

http://.../myController?a[val]=foo

在使用 JavaScript 时,HashMap 和嵌套对象当然没有区别,因此所有内容都将使用点方括号进行序列化。


问题:

我如何/在哪里可以告诉 Spring(或 Spring Boot,如果这更容易的话)对 both、嵌套对象 Maps 使用点表示法(或方括号)?

或者有什么理由让 Spring 在这些类型之间产生差异?

【问题讨论】:

    标签: spring spring-mvc spring-boot spring-4 http-request-parameters


    【解决方案1】:

    得益于其自定义的DataBinder 子类RelaxedDataBinder,Spring Boot 支持使用点分隔路径来绑定地图。好消息是它也是一个DataBinder,在 Spring MVC 中用于执行请求参数绑定。坏消息是插入你自己的活页夹并不简单,它需要是WebDataBinder。您可以通过声明您自己的名为requestMappingHandlerAdapterRequestMappingHandlerAdapter bean 来插入一个。例如:

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdpter() {
        return new RequestMappingHandlerAdapter() {
    
            @Override
            protected InitBinderDataBinderFactory createDataBinderFactory(
                    List<InvocableHandlerMethod> binderMethods)
                    throws Exception {
                return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()) {
    
                    @Override
                    protected ServletRequestDataBinder createBinderInstance(
                            final Object target, String objectName,
                            NativeWebRequest request) {
    
                        return new ServletRequestDataBinder(target) {
    
                            private RelaxedDataBinder relaxedBinder = new RelaxedDataBinder(target);
    
                            @Override
                            protected void doBind(MutablePropertyValues mpvs) {
                                this.relaxedBinder.bind(mpvs);
                            }
                        };
                    }
                };
            }   
        };
    }
    

    您可能很想重构它以避免使用多个嵌套的匿名内部类,但它希望能说明一般方法。

    【讨论】:

    • 哇!谢谢你。这段代码看起来真的很像 sh*t,但它有效!!:)
    • 现在我注意到,这会产生很多其他错误:( Spring 的Pageable 将不再起作用,因为Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface。似乎所有这些模块都不会被 Spring Boot 自动注册。
    • 如果它使用RelaxedDataBinder,那么它将支持不区分大小写的枚举转换,但它不支持。
    【解决方案2】:
    @InitBinder
    private void initBinder(WebDataBinder binder, ServletRequest request) {
        new RelaxedDataBinder(binder.getTarget()).bind(new ServletRequestParameterPropertyValues(request));
    }
    

    这就是我摆脱它的方式;控制器中委托给RelaxedDataBinder的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 2021-12-24
      相关资源
      最近更新 更多