【问题标题】:Spring MVC + DataTables: bind array[] parameterSpring MVC + DataTables:绑定数组 [] 参数
【发布时间】:2017-01-26 21:52:39
【问题描述】:

问题简述

我无法将 ...?name=John&sports[]=Volleyball&sports[]=Volleyball 之类的 url 参数转换为 Spring MVC 中的以下 Java 类。

class PersonFilter {       
    String name;
    List<String> sports; 
}

我需要在 sports[] 参数名称中使用括号。

长问题描述

我使用带有 Spring MVC 的 DataTables 框架。 我需要向 datatables ajax 请求添加一些复杂的过滤器属性,如下所示:

$('#table').DataTable({
    serverSide: true,
    ajax: {
        url: "https://server/some/path",
        data: function (data) {
            data.name = "John",
            data.sports = ["Football", "Volleyball"]
        }
    }
});

使用排序(参数 ordercolumns)和自定义过滤器(参数 namesports[])创建的 DataTables 请求如下所示:

https://server/some/path?name=John&sports[]=Volleyball&sports[]=Football&columns[0][name]=name&order[0][column]=0&order[0][dir]=asc

我需要将 URL 参数绑定到包含 java.util.List 的 PersonFilter 类。

class PersonFilter {       
    String name;
    List<String> sports; 
}

控制器看起来像:

@Controller
class PersonController {

    @RequestMapping(path = "/search")
    public List<Person> search(PersonFilter personFilter) {
       ...
    }
}

Spring 允许使用以下语法进行列表绑定 (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#beans-beans-conventions):

1) sports=Volleyball&sports=Football -> OK
2) sports[0]=Volleyball&sports[1]=Football -> OK

但是 Spring 不能将名称 sports[] 的属性转换为 List。

sports[]=Volleyball&sports[]=Football -> FAILs (expects index in brackets)

java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.8.0_121]
    at java.lang.Integer.parseInt(Integer.java:592) ~[na:1.8.0_121]
    at java.lang.Integer.parseInt(Integer.java:615) ~[na:1.8.0_121]
    at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:354) ~[spring-beans-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:280) ~[spring-beans-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95) ~[spring-beans-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:859) ~[spring-context-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.validation.DataBinder.doBind(DataBinder.java:755) ~[spring-context-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:192) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:106) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:150) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:114) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:160) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.6.jar:8.5.6]

我尝试过的:

使用 JQuery ajax 设置jQuery.ajaxSettings.traditional = true。 但在这种情况下,用于排序(顺序、列)的 DataTables 参数会导致 [Object],因此我无法解析排序参数。

https://server/some/path?name=John&sports=Volleyball&sports=Football&columns=[object Object]&order=[object Object]

感谢您的任何建议!

【问题讨论】:

    标签: java jquery spring spring-mvc datatables


    【解决方案1】:

    最后我决定在过滤器中修改请求的参数。

    我创建了HttpServletRequestWrapper,它从请求参数中删除了括号[]

    public class JQueryArrayParameterRequestWrapper extends HttpServletRequestWrapper {
        public static final String BRACKETS_SUFFIX = "[]";
    
        private Map<String, String[]> _parameterMap;
    
        public JQueryArrayParameterRequestWrapper(HttpServletRequest request) {
            super(request);
        }
    
        @Override
        public Map<String, String[]> getParameterMap() {
            if (_parameterMap == null) {
                _parameterMap = modifyParameterMap(super.getParameterMap());
            }
            return _parameterMap;
        }
    
        @Override
        public Enumeration<String> getParameterNames() {
            return Collections.enumeration(getParameterMap().keySet());
        }
    
        @Override
        public String[] getParameterValues(String name) {
            return getParameterMap().get(name);
        }
    
        @Override
        public String getParameter(String name) {
            return getParameterValues(name) != null && getParameterValues(name).length > 0 ? getParameterValues(name)[0] : null;
        }
    
        private Map<String, String[]> modifyParameterMap(Map<String, String[]> oldParameterMap) {
            Map<String, String[]> newParameterMap = new HashMap<>(oldParameterMap.size());
            for (Map.Entry<String, String[]> entry : oldParameterMap.entrySet()) {
                String key = entry.getKey();
                if (key.endsWith(BRACKETS_SUFFIX)) {
                    // remove brackets from parameter name
                    String newKey = key.substring(0, key.length() - BRACKETS_SUFFIX.length());
                    newParameterMap.put(newKey, entry.getValue());
                } else {
                    // leave parameter unmodified
                    newParameterMap.put(key, entry.getValue());
                }
            }
            return newParameterMap;
        }
    }
    

    然后我创建了过滤器:

    public class JQueryArrayParameterFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            /* empty */
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            chain.doFilter(new JQueryArrayParameterRequestWrapper((HttpServletRequest) request), response);
        }
    
        @Override
        public void destroy() {
            /* empty */
        }
    }
    

    最后我为 DataTables 请求注册了过滤器。

    @Configuration
    public class AppConfiguration { 
        @Bean
        public JQueryArrayParameterFilter jQueryArrayParameterFilter() {
            return new JQueryArrayParameterFilter();
        }
    
        @Bean
        public FilterRegistrationBean filterRegistrationBean(JQueryArrayParameterFilter filter) {
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
            filterRegistrationBean.setFilter(filter);
            filterRegistrationBean.addUrlPatterns( "/datatables/*");
            return filterRegistrationBean;
        }
    }
    

    【讨论】:

      【解决方案2】:
        data: function (data) {
              data.name = "John",
              data.sports = ["Football", "Volleyball"]
              data = JSON.stringify(data);
      }
      

      在发送上述代码的请求之前尝试对对象进行字符串化。

      【讨论】:

      • 这个解决方案很好。但我使用自定义 DataTablesMethodArgumentReslover 进行数据表排序和分页,如 Spring Sort one SortHandlerMethodArgumentResolver。当所有内容都打包在 JSON 中时,我不能使用现有的 DataTablesMethodArgumentResolver。对这个问题有什么建议吗?
      • 对不起,我不太明白,DataTablesMethodArgumentReslover 是java类吗?您应该建模一个封装 js 框架发送的 http 请求的 java 类,然后按照描述对所有内容进行字符串化,Spring 将自动将您的请求绑定到 2 对象的实例中作为方法的输入,即.....(PersonFilter personFilter , AnotherObjectForDatatableRequest anthe)
      • 是的,DataTablesMethodArgumentReslover 是一个类。问题是当我对请求进行字符串化时。 Spring 只绑定一个由@RequestBody 注释的方法参数。与 url 请求参数绑定不同,不可能使用多个 @RequestBody 注释来解析请求正文中的方法参数。第二个原因是,DataTablesMethodArgumentReslover 类只能从参数绑定方法参数(如 SortHandlerMethodArgumentResolver 类的 Spring 实现)。
      猜你喜欢
      • 2015-03-27
      • 1970-01-01
      • 2016-01-25
      • 2014-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      相关资源
      最近更新 更多