JSON parse error: Unexpected character (‘%‘ (code 37)): expected a valid value (number, String, arr

本文详细探讨了Spring MVC框架中不同HTTP请求方式下数据的处理方法,包括GET和POST请求如何与控制器方法交互,以及@RequestBody注解的作用。通过具体的实践案例,分析了不同请求头类型对数据解析的影响。

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

1 复现过程 POST  + @RequestBody + json格式的数据,不指定数据格式

MappingJackson2HttpMessageConverter 读取 application/x-www-form-urlencoded;charset=UTF-8 就会报异常:JSON parse error: Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

项目配置的参数转换器配置如下:指定MappingJackson2HttpMessageConverter 解析 application/x-www-form-urlencoded 数据

debug 可以定位到源码:

org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#readJavaType

最终会调用com.fasterxml.jackson.databind.ObjectMapper#readValue(java.io.InputStream, com.fasterxml.jackson.databind.JavaType)

其中 InputStream 内容为 %7B%22keyWord%22%3A%22xx%22%2C%22offset%22%3A0%2C%22size%22%3A10%7D=

解码后是KV形式:{"keyWord":"xx","offset":0,"size":10}=

2 根本原因

POST请求传入了json格式的数据,但是却没有指定数据格式(默认) -H "Content-Type:application/x-www-form-urlencoded;charset=UTF-8",引导jackson来解析kv格式的表单数据!

这就超级**了! 正确做法是指定 content-type: application/json如下图(项目中指定MappingJackson2HttpMessageConverter 解析 application/x-www-form-urlencoded 数据)

 

 

 

3  GET 请求 + @RequestParam 

参数 curl -X GET '127.0.0.1:8080/add/employee?nameee=12&empId=0' 从注解中获取参数名如,KV

org.springframework.web.bind.annotation.RequestParam

4  GET 请求 + @RequestBody + 不指定content-type 也可以传输json数据

输入:curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

@RequestMapping(value = "/list", method = RequestMethod.GET)
public SearchRequest getHabitTitleMatchList(@RequestBody SearchRequest searchRequest,HttpServletRequest httpServletRequest) {

      return searchRequest;

}

输出:{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% 符合预期

   GET 不指定content-type 默认 application/x-www-form-urlencoded 

   先去根据表单---->如果没有--->再去找body(前提项目中指定MappingJackson2HttpMessageConverter 解析 application/x-www-form-urlencoded 数据 )

匹配后还是用jackson可以解析http协议传输的data。如果指定content-type:application/json,当然更合理的顺利执行。

 

 

  • 结论 :对于GET请求只要,能匹配到MappingJackson2HttpMessageConverter 并且传输 data body为json格式就能,解析出符合预期的入参。

5  POST/GET 无 RequestBody注解并且非简单类型相当于给参数加了@ModelAttribut

A curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'

{"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% 符合预期从表单找数据

B curl -X GET -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'

// {"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% 符合预期 指定content-type 无效,依然从表单找数据

C curl -X POST -H 'content-type:application/json' '127.0.0.1:8080/list?keyWord=1233'

{"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 有/无@ModelAttribute 符合预期从表单找数据

D curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

{"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 有/无@ModelAttribute 符合预期从表单找数据

E curl -X POST -H 'content-type:application/json' '127.0.0.1:8080/list?keyWord=1233'

{"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 无 @ModelAttribute 符合预期从表单找数据

解释:

(SearchRequest searchRequest)非简单类型 和@ModelAttribute 类似 方法签名决定了解析器-->ModelAttributeMethodProcessor

原因:construction---> Bean property binding and validation -->

org.springframework.web.method.annotation.ModelAttributeMethodProcessor#bindRequestParameters--->构造参数键值对

org.springframework.beans.MutablePropertyValues#MutablePropertyValues(java.util.Map)--->coyote 封装了请求参数
  • 结论:GET请求参数直接使用@ModelAttribute 接受参数就OK了

6 POST/GET 有RequestBody注解 POST 必须指定 content-type:application/json

经由:

@RequestMapping(value = "/list", method = RequestMethod.POST/GET)

public SearchRequest getHabitTitleMatchList(@RequestBody SearchRequest searchRequest) {// RequestBody 方法签名决定了解析器jackson

return searchRequest;

}

A curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'

// 无异常 但是不符合预期 {"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% GET提交表单-->提交解析url中参数异常(因为GET 先去根据表单---->如果没有--->再去找body)(前提 jackson 支持类型中有 application/x-www-form-urlencoded )

B curl -X GET -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'

// 无异常{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% GET 根据content直接去找body

C curl -X GET -H 'content-type:application/json' '127.0.0.1:8080/list?keyWord=1111111' 指定了content-type 必须要body(前提 jackson 支持类型中有 application/x-www-form-urlencoded )

// Required request body is missing: public com.xiaodaka.rec.face.aspectj.SearchRequest

D curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

// 无异常 {"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% GET 先去根据表单---->如果没有--->再去找body(前提 jackson 支持类型中有 application/x-www-form-urlencoded )

curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233' jackson-->解析body

// 无异常

curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

// com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'keyWord' 表单-->提交解析url中参数异常

curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

// nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('%' (code 37)) 表单-->skip(提交解析url中参数无)--->解析body异常
  • 结论:一定要指定content-type 即使知道使用默认的content-type,GET 请求url不带参数但是带了body也是可以jackson解析的

7 其他情况列举

curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

{"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}%

curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

{"keyWord":null,"offset":0,"size":100,"sort":0,"order":0}%

改为(@RequestBody SearchRequest searchRequest) 后:

curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}%

curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}%

// 取消类型 application/x-www-form-urlencoded

curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

{"timestamp":"2020-11-21","status":415,"error":"Unsupported Media Type","message":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported","path":"/list"}%

// 打开类型 application/x-www-form-urlencoded

curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'

{"timestamp":"2020-11-21","status":400,"error":"Bad Request","message":"JSON parse error: Unrecognized token 'keyWord': was expecting ('true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'keyWord': was expecting ('true', 'false' or 'null')\n at [Source: (ByteArrayInputStream); line: 1, column: 9]","path":"/list"}%

curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list' 但是(controller和curl method)改为GET就行了!!!

{"timestamp":"2020-11-21","status":400,"error":"Bad Request","message":"JSON parse error: Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('%' (code 37)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n at [Source: (ByteArrayInputStream); line: 1, column: 2]","path":"/list"}%

curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'

{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% // 符合预期

GET 协议一起传输 POST 放到body中??? POST 必须指定content-type; GET 提交 --data数据不是body传输,但是可以用jackson解析

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自驱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值