seaswalker / posts

0 stars 0 forks source link

Spring post form data #54

Open seaswalker opened 3 years ago

seaswalker commented 3 years ago

微信等回调有的使用post form data的请求形式,并且参数字段为snake格式,而不是Java常用的camel case。

首先定义一个filter:

public class SnakeToCamelCaseFilter extends 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 = snakeToCamel(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
    );
  }

  private static String snakeToCamel(final String snake) {
    if (StringUtils.isEmpty(snake)) {
      return snake;
    }
    final StringBuilder sb = new StringBuilder(snake.length() + snake.length());
    for (int i = 0; i < snake.length(); i++) {
      final char c = snake.charAt(i);
      if (c == '_') {
        sb.append(
          (i + 1) < snake.length()
            ? Character.toUpperCase(snake.charAt(++i))
            : ""
        );
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }
}

之后在Spring中配置此filter:

@Bean
public FilterRegistrationBean<SnakeToCamelCaseFilter> snakeToCamelCaseFilter() {
    FilterRegistrationBean<SnakeToCamelCaseFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new SnakeToCamelCaseFilter());
    registrationBean.addUrlPatterns("/api/v1/firstName");
    registrationBean.setOrder(1);
    return registrationBean; 
}

而后可以定义controller:

@PostMapping(value = "/firstName", consumes = MediaType.APPLICATION_FORM_URLENCODED)
public String firstName(@Valid @ModelAttribute Domain domain) {
    return "success";
}

@Data
public class Domain {
    private String firstName;
}

这样以下面这样的格式请求,firstName也会被赋上值,并且参数校验同样起作用:

POST http://localhost:8080/firstname
Content-Type: application/x-www-form-urlencoded

first_name=skywalker

snake转为camel参考自: CamelCase と snake_case を相互変換する 原代码当请求参数名并不是snake而是camel时,如firstName,会转换为firstname,Google guava也有同样的问题。

Spring相关issue: Add @FormAttribute attributes to customize x-www-form-urlencoded [SPR-13433]

seaswalker commented 3 years ago

@ModelAttribute注解非必须