springboot 参数转换

本文深入探讨了在SpringBoot和SpringMVC中处理请求参数转换的多种方法,包括使用@InitBinder、@DateTimeFormat、Converter接口等,并通过实例展示了如何解决日期类型参数的转换问题,同时介绍了处理数组和复杂对象的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用springboot或者说在使用springmvc时,很多人都会遇到请求到达controller时,如何将Request中的数据传递到对应的方法上,但是我想很多人都会遇到一些关于参数转换的问题,主要包括两个方面:

1、请求中字符串类型的参数如何转换成controller对应方法的参数

2、请求数据格式与参数填充问题

怎么理解上面提出的两点,举个例子,当我们请求为字符串类型时,而参数是java.util.Date,那么能够正常解析并响应吗?这就是第一点的问题;如果我们需要获取字符串数字类型参数,请求中应该如何定义请求参数,这就是第二种问题。下面来分析下具体示例:

首先搭建一个springboot的项目,这么没什么说的,我们先不做任何配置,只用写一个controller,然后启动项目:

@RestController
@RequestMapping
public class TestControler {

    @GetMapping
    public Object test(@RequestParam("date") Date date){
        return date;
    }
}

通过postman发送请求:

GET  localhost:8080?date=2019-07-26

不出任何意外的出现错误:

{
    "timestamp": 1564147155806,
    "status": 400,
    "error": "Bad Request",
    "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.util.Date] for value '2019-07-26'; nested exception is java.lang.IllegalArgumentException",
    "path": "/"
}

一般我们会怎么解决:

1、通过 @InitBinder

@InitBinder
public void initBinding(WebDataBinder dataBinder){
    dataBinder.registerCustomEditor(Date.class, new DatePropertyEditor() );
}
@Slf4j
public class DatePropertyEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = sdf.parse(text);
        } catch (ParseException e) {
            log.error("",e);
        }
        super.setValue(date);
    }
}

2、通过 @DateTimeFormat(pattern = "yyyy-MM-dd")

@GetMapping
public Object test(@RequestParam("date")
                   @DateTimeFormat(pattern = "yyyy-MM-dd")
                   Date date){
    return date;
}

3、实现接口Converter<String,Date>

@Slf4j
@Component
public class StringDateConverter implements Converter<String,Date> {
    @Override
    public Date convert(String text) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return sdf.parse(text);
        } catch (ParseException e) {
            log.error("",e);
        }
        return null;
    }
}

4、实现接口ConverterFactory<String,Date>

@Slf4j
public class StringToDateConverterFactory implements ConverterFactory<String,Date> {

    @Override
    public <T extends Date> Converter<String,T> getConverter(Class<T> clazz) {

        return new StringToDate<T>(clazz);
    }

    private static class StringToDate<T extends Date> implements Converter<String,T> {
        private Class<T> targetType;

        public StringToDate(Class<T> targetType) {
            this.targetType = targetType;
        }

        @Override
        public T convert(String source) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date date = sdf.parse(source);
                return (T) date;
            } catch (ParseException e) {
                log.error("",e);
            }
            return null;
        }
    }
}

注册到容器:

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToDateConverterFactory());
    }
}

5、实现接口 GenericConverter

这种方法比较复杂,没有研究过。

6、HandlerMethodArgumentResolver

通过HandlerMethodArgumentResolver我们可以将请求中的数据封装成指定类型,在对请求组装成目标类型数据时,该方法是非常有用的,同时在项目中使用概率也是比较高。

看一下示例,将前台提交的数据组装成指定类型Request

@Data
public class Request {

    private String id;
    private String method;
    private String date;
    private String name;
    private String description;
}
public class RequestDataHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestData.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Class<?> paramClazz = parameter.getParameterType();
        Object param = BeanUtils.instantiate(paramClazz);

        Iterator<String> reqParams = webRequest.getParameterNames();
        Map<String,String> paramMap = new HashMap<>();
        while (reqParams.hasNext()){
            String propKey = reqParams.next();
            paramMap.put(propKey,webRequest.getParameter(propKey));
        }

        BeanInfo beanInfo = Introspector.getBeanInfo(paramClazz);
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        for(PropertyDescriptor pd : pds){
            String prop = pd.getName();
            Class<?> propType = pd.getPropertyType();
            Method setMethod = pd.getWriteMethod();
            if(setMethod!=null && propType == String.class)
                setMethod.invoke(param,paramMap.get(prop));
        }
        return param;
    }
}
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {


    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new RequestDataHandlerMethodArgumentResolver());
    }
}

测试:

    @PostMapping("/request")
    public Object request(@RequestData Request request){
        return request;
    }

这样我们请求中的数据则会自动转换成Request对象了。

总结:需要特别说明一点的是,如果通过@EnableWebMvc时,通过Converter相关接口时,需要通过方法4中的方式注册到容器,否则将不生效,具体原因还需要对比两种配置的区别。

参考:https://blog.youkuaiyun.com/fsp88927/article/details/37692215

 

下面看下关于参数转换一些问题 ,一般如果接受参数类型为数组,比如string[] array,比如下面这样的:

@PostMapping("/array")
public Object array(@RequestBody String[] arr){
    return arr;
}

我们知道,前端在向后台发送请求,常用的一般有两种:application/x-www-form-urlencoded , application/json

针对第一种,我们只需要将参数构建成 arr=1&arr=2....这样的形式即可,比如在已数组作为参数时,一般浏览器都会转换成arr[]=1 arr[]=2...其实这个时候通过@RequestParam("arr[]")就可以了,毕竟js对象不能有相同的key。

如果是json的呢,那么没有办法,只能将数组放到对象中:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Record {

    private String id;

//    @JsonFormat
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date date;

    private String[] arr;
}
@PostMapping("/record")
public Object record(@RequestBody Record record){
    return record;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值