You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Controller请求参数与对象映射:参数名不一致的解决方法

解决Spring MVC请求参数下划线转驼峰映射及数组绑定问题

首先直接回答你的第一个问题:不需要自定义HandlerMethodArgumentResolver,Spring MVC本身提供了更简洁的配置方式来处理下划线命名的请求参数到驼峰属性的映射,而且能完美支持数组/集合类型的绑定,比你用Map转DTO的临时方案更可靠。

方案一:全局配置下划线转驼峰参数绑定

Spring MVC的请求参数绑定到JavaBean时,依赖DataBinder处理属性映射。我们可以通过全局配置,让DataBinder自动将下划线命名的参数(如event_id)转换为驼峰命名的属性(如eventId)。

实现步骤:

  1. 创建一个WebConfig配置类,实现WebMvcConfigurer,自定义WebBindingInitializer来设置属性命名转换规则:
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public ConfigurableWebBindingInitializer webBindingInitializer() {
        return new ConfigurableWebBindingInitializer() {
            @Override
            public void initBinder(WebDataBinder binder) {
                super.initBinder(binder);
                // 自定义属性访问器,手动实现下划线转驼峰
                binder.setPropertyAccessor(new BeanWrapperImpl() {
                    @Override
                    public void setPropertyValue(String propertyName, Object value) {
                        String camelCaseProperty = underscoreToCamel(propertyName);
                        super.setPropertyValue(camelCaseProperty, value);
                    }

                    // 自定义下划线转驼峰工具方法
                    private String underscoreToCamel(String input) {
                        StringBuilder sb = new StringBuilder();
                        boolean nextUpper = false;
                        for (char c : input.toCharArray()) {
                            if (c == '_') {
                                nextUpper = true;
                            } else if (nextUpper) {
                                sb.append(Character.toUpperCase(c));
                                nextUpper = false;
                            } else {
                                sb.append(c);
                            }
                        }
                        return sb.toString();
                    }
                });
            }
        };
    }
}
  1. 配置完成后,你原来的Controller代码不需要任何修改,就能直接处理event_id这类参数:
@GetMapping("/contacts") 
public ContactDTO contacts(ContactDTO contact) { 
    return contact; 
}

此时localhost:8080/contacts?id=22&name=John&event_id=11的请求会正确映射到ContactDTOeventId属性,同时数组/集合类型的参数(如tags=java&tags=spring)也能自动绑定到DTO的List<String> tags属性,完全兼容原有功能。

方案二:优化你的临时Map转DTO方案

如果你暂时不想修改全局配置,可以优化现有的Map转DTO方案,解决数组绑定失效的问题:

问题根源:

原来的@RequestParam Map<String,String> params只会获取每个参数的最后一个值,当请求是数组类型(如ids=1&ids=2)时,Map中只会保存ids=2,导致objectMapper无法转换为List<Long>

优化后的代码:

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.List;

@RestController
public class ContactController {

    @GetMapping("/contacts") 
    public ContactDTO contacts(@RequestParam Map<String, List<String>> params) { 
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置Jackson支持下划线转驼峰的命名策略
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        return objectMapper.convertValue(params, ContactDTO.class);
    }
}

注:这里给Jackson配置SNAKE_CASE命名策略,才能把event_id的key转换为eventId属性;同时用Map<String, List<String>>接收参数,Spring会自动将相同key的多个参数封装为List,避免数组参数丢失。

不过这个方案的局限性在于:每个使用该方式的Controller方法都需要重复这段代码,而且无法处理嵌套对象的绑定,所以更推荐方案一的全局配置方式。

为什么@Json注解无效?

你提到的@JsonAttribute(应为@JsonProperty)只在Jackson处理@RequestBody的JSON请求体时生效,而URL请求参数的绑定是由Spring MVC的DataBinder负责的,所以Jackson的注解对URL参数绑定不起作用,这也是为什么需要单独配置Spring MVC的参数绑定规则。


内容的提问来源于stack exchange,提问作者Ivan

火山引擎 最新活动