当前位置:   article > 正文

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

json parse error: unexpected character

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 可以定位到源码:

  1. org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#readJavaType
  2. 最终会调用com.fasterxml.jackson.databind.ObjectMapper#readValue(java.io.InputStream, com.fasterxml.jackson.databind.JavaType)
  3. 其中 InputStream 内容为 %7B%22keyWord%22%3A%22xx%22%2C%22offset%22%3A0%2C%22size%22%3A10%7D=
  4. 解码后是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数据

  1. 输入:curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  2. @RequestMapping(value = "/list", method = RequestMethod.GET)
  3. public SearchRequest getHabitTitleMatchList(@RequestBody SearchRequest searchRequest,HttpServletRequest httpServletRequest) {
  4. return searchRequest;
  5. }
  6. 输出:{"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

  1. A curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'
  2. {"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% 符合预期从表单找数据
  3. B curl -X GET -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'
  4. // {"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% 符合预期 指定content-type 无效,依然从表单找数据
  5. C curl -X POST -H 'content-type:application/json' '127.0.0.1:8080/list?keyWord=1233'
  6. {"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 有/无@ModelAttribute 符合预期从表单找数据
  7. D curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  8. {"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 有/无@ModelAttribute 符合预期从表单找数据
  9. E curl -X POST -H 'content-type:application/json' '127.0.0.1:8080/list?keyWord=1233'
  10. {"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}% 无 @ModelAttribute 符合预期从表单找数据
  11. 解释:
  12. (SearchRequest searchRequest)非简单类型 和@ModelAttribute 类似 方法签名决定了解析器-->ModelAttributeMethodProcessor
  13. 原因:construction---> Bean property binding and validation -->
  14. org.springframework.web.method.annotation.ModelAttributeMethodProcessor#bindRequestParameters--->构造参数键值对
  15. org.springframework.beans.MutablePropertyValues#MutablePropertyValues(java.util.Map)--->coyote 封装了请求参数
  • 结论:GET请求参数直接使用@ModelAttribute 接受参数就OK了

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

  1. 经由:
  2. @RequestMapping(value = "/list", method = RequestMethod.POST/GET)
  3. public SearchRequest getHabitTitleMatchList(@RequestBody SearchRequest searchRequest) {// RequestBody 方法签名决定了解析器jackson
  4. return searchRequest;
  5. }
  6. A curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'
  7. // 无异常 但是不符合预期 {"keyWord":"1111111","offset":0,"size":100,"sort":0,"order":0}% GET提交表单-->提交解析url中参数异常(因为GET 先去根据表单---->如果没有--->再去找body)(前提 jackson 支持类型中有 application/x-www-form-urlencoded )
  8. B curl -X GET -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1111111'
  9. // 无异常{"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% GET 根据content直接去找body
  10. 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 )
  11. // Required request body is missing: public com.xiaodaka.rec.face.aspectj.SearchRequest
  12. D curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  13. // 无异常 {"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% GET 先去根据表单---->如果没有--->再去找body(前提 jackson 支持类型中有 application/x-www-form-urlencoded )
  14. 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
  15. // 无异常
  16. curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  17. // com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'keyWord' 表单-->提交解析url中参数异常
  18. curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  19. // nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('%' (code 37)) 表单-->skip(提交解析url中参数无)--->解析body异常
  • 结论:一定要指定content-type 即使知道使用默认的content-type,GET 请求url不带参数但是带了body也是可以jackson解析的

7 其他情况列举

  1. curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  2. {"keyWord":"1233","offset":0,"size":100,"sort":0,"order":0}%
  3. curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  4. {"keyWord":null,"offset":0,"size":100,"sort":0,"order":0}%
  5. 改为(@RequestBody SearchRequest searchRequest) 后:
  6. curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  7. {"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}%
  8. curl -X POST -H 'content-type:application/json' --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  9. {"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}%
  10. // 取消类型 application/x-www-form-urlencoded
  11. curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  12. {"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"}%
  13. // 打开类型 application/x-www-form-urlencoded
  14. curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list?keyWord=1233'
  15. {"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"}%
  16. curl -X POST --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list' 但是(controller和curl method)改为GET就行了!!!
  17. {"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"}%
  18. curl -X GET --data '{"keyWord":"xx","offset":0,"size":10}' '127.0.0.1:8080/list'
  19. {"keyWord":"xx","offset":0,"size":10,"sort":0,"order":0}% // 符合预期
  20. GET 协议一起传输 POST 放到body中??? POST 必须指定content-type; GET 提交 --data数据不是body传输,但是可以用jackson解析

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/679575
推荐阅读
相关标签
  

闽ICP备14008679号