springboot参数转换
在使⽤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",
error parse new
"exception": "org.hod.annotation.MethodArgumentTypeMismatchException",
"message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is onvert.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){
}
@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) {
<("",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) {
<("",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) {
<("",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, WebDataBinderFactor        Class<?> paramClazz = ParameterType();
Object param = BeanUtils.instantiate(paramClazz);
Iterator<String> reqParams = ParameterNames();
Map<String,String> paramMap = new HashMap<>();
while (reqParams.hasNext()){
String propKey = ();
paramMap.put(Parameter(propKey));
}
BeanInfo beanInfo = BeanInfo(paramClazz);
PropertyDescriptor[] pds = PropertyDescriptors();
for(PropertyDescriptor pd : pds){
String prop = pd.getName();
Class<?> propType = pd.getPropertyType();
Method setMethod = pd.getWriteMethod();
if(setMethod!=null && propType == String.class)
setMethod.invoke((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中的⽅式注册到容器,否则将不⽣效,具体原因还需要对⽐两种配置的区别。
下⾯看下关于参数转换⼀些问题 ,⼀般如果接受参数类型为数组,⽐如string[] array,⽐如下⾯这样的:
@PostMapping("/array")
public Object array(@RequestBody String[] arr){
return arr;
}
我们知道,前端在向后台发送请求,常⽤的⼀般有两种:application/x-www-form-urlencoded , applic
ation/json
针对第⼀种,我们只需要将参数构建成 arr=1&这样的形式即可,⽐如在已数组作为参数时,⼀般浏览器都会转换成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;
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。