使⽤PathVariable注解获取路径中的参数出现HTTP状态400-
错误的请求
返回信息如下:
<body><h1>HTTP状态 400 - 错误的请求</h1>
<hr class="line"/>
<p><b>类型</b> 状态报告</p>
<p><b>描述</b> 由于被认为是客户端对错误(例如:畸形的请求语法、⽆效的请求信息帧或者虚拟的请求路由),服务器⽆法或不会处理当前请求。</p>
<hr class="line"/>
<h3>Apache Tomcat/9.0.41</h3></body>
情况⼀、获取Long类型的参数
在不使⽤@InitBinder注册⽤户的⾃定义的java.beans.PropertyEditor类型情况
Java代码如下:
@RestController
@RequestMapping(path = "v1/hello/")
public class HelloController {
@GetMapping(path = "showLong/{userId}")
public long showLong(@PathVariable long userId) {
return userId;
}
}
请求参数如下:
### 测试long类型
GET localhost:9090/v1/hello/showLong/12345L
Accept: application/json
源码跟踪关键流程。
org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, onvert.TypeDescriptor) ---->
org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframe ---->
onvert.support.GenericConversionService#convert(java.lang.Object, onvert.TypeDescriptor, org.springfram ---->
onvert.support.ConversionUtils#invokeConverter
---->
onvert.support.GenericConversionService.ConverterFactoryAdapter#convert
---->
onvert.support.StringToNumberConverterFactory.StringToNumber#convert
---->
org.springframework.util.NumberUtils#parseNumber(java.lang.String, java.lang.Class<T>)
org.springframework.util.NumberUtils#parseNumber(java.lang.String, java.lang.Class<T>)的源码如下:
public static <T extends Number> T parseNumber(String text, Class<T> targetClass) {
String trimmed = imAllWhitespace(text);
if (Byte.class == targetClass) {
return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));
}
else if (Short.class == targetClass) {
return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));
}
else if (Integer.class == targetClass) {
return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));
}
else if (Long.class == targetClass) {
return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));
}
else if (BigInteger.class == targetClass) {
return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));
}
else if (Float.class == targetClass) {
return (T) Float.valueOf(trimmed);
}
else if (Double.class == targetClass) {
return (T) Double.valueOf(trimmed);
}
else if (BigDecimal.class == targetClass || Number.class == targetClass) {
return (T) new BigDecimal(trimmed);
}
else {
throw new IllegalArgumentException(
"Cannot convert String [" + text + "] to target class [" + Name() + "]");
}
}
使⽤@InitBinder注册⽤户的⾃定义的java.beans.PropertyEditor类型情况Java代码如下:(这⾥我使⽤com.sun.beans.editors.LongEditor,也可⾃⼰实现)
@RestController
@RequestMapping(path = "v1/hello/")
public class HelloController {
@InitBinder
public void initBinder(WebDataBinder binder) {
}
@GetMapping(path = "showLong/{userId}")
public long showLong(@PathVariable long userId) {
return userId;
}
}
请求参数如下:
### 测试long类型
GET localhost:9090/v1/hello/showLong/12345L
Accept: application/json
源码跟踪的关键流程如下:
org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, onvert.TypeDescriptor) ---->
org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframe ---->
org.springframework.beans.TypeConverterDelegate#doConvertValue
---->
org.springframework.beans.TypeConverterDelegate#doConvertTextValue
org.springframework.beans.TypeConverterDelegate#doConvertTextValue的源码如下:
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
} catch (Exception var5) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + Class().getName() + "] does not support setValue call", var5);
}
}
editor.setAsText(newTextValue);
Value();
}
com.sun.beans.editors.LongEditor#setAsText的源码如下:
public void setAsText(String var1) throws IllegalArgumentException {
this.setValue(var1 == null ? null : Long.decode(var1));
}
仔细查看最终的参数转换都是使⽤Lond.decode(String value)⽅法或者Long.valueOf(String value)。⽽我们传递的参数是
12345L,这两个⽅法执⾏都会抛出
java.lang.NumberFormatException: For input string: "12345L"
⾄此Long类型的参数出现上述问题的根因出,解决⽅法也就明了了。
⽅式⼀:传递Long类型参数时不要时L标注参数类型,直接传递相应的数值即可。如下⽰例:
### 测试long类型
GET localhost:9090/v1/hello/showLong/12345
Accept: application/json
⽅式⼆:直接写⼀个实现java.beans.PropertyEditor接⼝或者继承com.sun.beans.editors.NumberEditor实现⼀个⾃定义的
Editor,将字符串后⾯的L或者l给删除后在使⽤Long.decode()和Long.valueOf()⽅法。
情况⼆:获取java.util.Date类型的参数
1、使⽤@PathVariable注解取路径中的参数值
Java代码如下:
@RestController
@RequestMapping(path = "v1/hello/")
public class HelloController {
@GetMapping(path = "showDate/{initDate}")
public Date showDate(@PathVariable Date initDate) {
return initDate;
}
}
请求如下:
### 测试Date类型
GET localhost:9090/v1/hello/showDate/2021-01-01 14:30:34
Accept: application/json
代码跟踪路径
org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, MethodParameter)
---->
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, onvert.TypeDescriptor) ---->
org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframe ---->
onvert.support.GenericConversionService#convert(java.lang.Object, onvert.TypeDescriptor, org.springfram ---->
onvert.support.ConversionUtils#invokeConverter
---->差异步骤,每个GenericConverter中的convert⽅法
onvert.support.ObjectToObjectConverter#convert
error parse new默认配置的情况下看流程都是都⼀个模版,⼀个差异为如下⽅法返回的对象不同。
onvert.support.GenericConversionService#getConverter
这个⽅法⽅法返回不同的verter.GenericConverter类型。例如当前这种传递参数是
java.lang.String类型,⽽接受类型是java.util.Date类型,其返回的结果是
onvert.support.ObjectToObjectConverter
在ObjectToObjectConverter对象中的convert⽅法中通过反射获取了⼀个Date(String str)的⼀个构造器,⽽java.util.Date没有相应
的构造器,从⽽导致出现java.lang.IllegalArgumentException。
所以这种情况我⽬前只知道使⽤官⽹提供的使⽤@InitBinder注解注册⼀个
org.springframework.beans.propertyeditors.CustomDateEditor,当然也可以是⾃⼰实现的。
解决⽅案代码如下:
@RestController
@RequestMapping(path = "v1/hello/")
public class HelloController {
private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@InitBinder
public void initBinder(WebDataBinder binder) {
}
@GetMapping(path = "showDate/{initDate}")
public Date showDate(@PathVariable Date initDate) {
return initDate;
}
}
使⽤@InitBinder注解后其执⾏流程就合上述的Long类型⼀致了,这⾥就不再介绍⼀次了。需要注意的是传递的参数格式必须和DateFormat的pattern的格式⼀致,不然也是会报错。
2、使⽤@RequestBody注解时POJO对象中有java.util.Date类型的属性
Java代码如下:(前提是引⼊了jackson的相关核⼼依赖)
package org.jackson.llers.hello;
import org.jackson.mvc.web.po.RequestInfo;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(path = "v1/hello/")
public class HelloController {
@PostMapping(path = "showRequestInfo")
public RequestInfo showRequestInfo(@RequestBody RequestInfo requestInfo) {
return requestInfo;
}
}
RequestInfo定义如下:
package org.jackson.mvc.web.po;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
public class RequestInfo implements Serializable {
private String username;
private Long userId;
private int userAge;
private double userHeight; // ⾝⾼
private Date birthday;
private BigDecimal balance; // 余额
// 省略get和set⽅法
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论