【问题标题】:Binding snake_case request parameters to a Spring form将 snake_case 请求参数绑定到 Spring 表单
【发布时间】:2016-04-28 23:06:25
【问题描述】:

我正在使用 Spring Boot 实现一个简单的 RESTful 服务,其接口由 .NET(我认为)客户端定义。它们的参数名称是snake_case,而不是camelCase,这显然意味着我需要自定义它们的映射方式。

对于 JSON 输入/输出,没关系,我刚刚自定义了 ObjectMapper,如下所示:

@Bean
public ObjectMapper objectMapper() {
  ObjectMapper objectMapper = new ObjectMapper();
  objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
  return objectMapper;
}

效果很好。现在我的问题是表单数据。我有一个像这样的 Spring 表单:

public class MyForm {
  private String myValue;

  public String getMyValue() {return myValue;}
  public void setMyValue(String myValue) {this.myValue = myValue;}
}

但我需要接受的请求如下所示:

POST /foo/bar HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

my_value=5

我觉得 Spring 的绑定中必须有一些简单的钩子,就像 Jackon 的 ObjectMapper 中的等效设置,但我正在努力寻找一个。我可以在这里找到的唯一有点相关的帖子是this one, about completely changing the parameter names,其中的一些建议对我的用例来说似乎有些过分了。

简单的解决方案就是对MyForm中的字段使用snake case,效果很好,但是有点丑。

我在其他地方看到的最后一个建议是使用拦截器在进入的过程中修改请求参数,这看起来很简单,但感觉肯定会有例外,使它变得不平凡,而且我担心将代码隐藏在拦截器中会使您在遇到一个不起作用的晦涩案例时很难找到。

是否有一些“正确”的 Spring-y 方式来处理我缺少的这个问题,还是我只需要选择上述不太完美的解决方案之一?

【问题讨论】:

  • 嗨,我正在努力解决与您描述的相同的问题。有没有机会分享一个可行的解决方案?
  • 对不起,没有。我最终只是让表单具有像 my_value 这样的字段名称,然后是像 getMy_value() 和 setMy_value() 这样的 getter/setter。不是很好,但很有效。
  • 是的,我知道它有效,但不是真正的 Java 约定.. 太糟糕了,谢谢!
  • @DaveyDaveDave 我不想这么说,但我做了同样的事情。我的响应就像一个魅力,但没有上述“黑客”的请求也没有骰子。

标签: spring spring-mvc


【解决方案1】:

可能你已经解决了这个问题,我今天正在解决这个问题并在StackOverflow PT上回答了一个问题。

所以这是交易:

在请求到达控制器之前创建一个要执行的过滤器,并相应地格式化参数(在我的场景中从蛇案例到骆驼案例)。

说话很便宜,给我看代码!

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;

import com.google.common.base.CaseFormat;

@Configuration
public class AppConfig {

    @Bean
    public Filter snakeConverter() {
        return new OncePerRequestFilter() {

            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
                final Map<String, String[]> formattedParams = new ConcurrentHashMap<>();

                for (String param : request.getParameterMap().keySet()) {
                    String formattedParam = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, param);
                    formattedParams.put(formattedParam, request.getParameterValues(param));
                }

                filterChain.doFilter(new HttpServletRequestWrapper(request) {
                    @Override
                    public String getParameter(String name) {
                        return formattedParams.containsKey(name) ? formattedParams.get(name)[0] : null;
                    }

                    @Override
                    public Enumeration<String> getParameterNames() {
                        return Collections.enumeration(formattedParams.keySet());
                    }

                    @Override
                    public String[] getParameterValues(String name) {
                        return formattedParams.get(name);
                    }

                    @Override
                    public Map<String, String[]> getParameterMap() {
                        return formattedParams;
                    }
                }, response);
            }
        };
    }

}

snakeConverter 施展魔法。

在那里,doFilterInternal 总是在请求到达控制器之前执行,参数以其格式化形式存储在新的Map 中,并通过filterChain.doFilter 转发到控制器。

HttpServletRequestWrapper 负责向控制器提供我们的新参数。

此代码完全基于azhawkes 过滤器。


使用以下 URL 中的简单控制器对其进行测试:http://localhost:8080/snakecase?foo_bar=123

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    相关资源
    最近更新 更多