众所周知,springmvc中@RequestBody的注解是一个很实用的功能,它能帮我们解析客户端(移动设备、浏览器等)发送过来的json数据,并封装到实体类中。
但我今天要说的不是它的原理,而是记录一些工作中使用@RequestBody注解遇到的一些问题,也提醒广大java开发者避免类似的问题。
最近有个需求,接收客户的设备发送过来的json数据,客户的设备里面只能修改ip,然后通过http协议的post方式发送数据过来。我很自然地想到在登录页那里处理,在toLogin方法中增加@RequestBody Kehu kehu参数,用户解析并封装json数据。
废话不多说,上代码,如下所示:
@RequestMapping(value = "/toLogin") public ModelAndView toLogin(HttpServletRequest request, @RequestBody Kehu kehu) throws Exception { // 接收客户设备发送过来的json数据 if (kehu != null && !StringUtil.isEmpty(kehu.cmd)) { uploadData(kehu); } ModelAndView mv = new ModelAndView(); PageData pageData = this.getPageData(request); pageData.put("SYSNAME", Tools.readTxtFile(Const.SYSNAME)); // 读取系统名称 mv.setViewName("base/login"); mv.addObject("pd", pageData); return mv; }
一切看似很完美,在浏览器上测试一下,输入localhost(我的项目已经设置为了缺省项目,端口号也改为了80)
我傻眼了,报了400错误。如下图所示:
The request sent by the client was syntactically incorrect.
翻译过来就是:客户端发送的请求在语法上是不正确的。
没加@RequestBody Kehu kehu之前是正常的,问题肯定出在了这里。我一看RequestBody的源码:
package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.http.converter.HttpMessageConverter; /** * Annotation indicating a method parameter should be bound to the body of the web request. * The body of the request is passed through an {@link HttpMessageConverter} to resolve the * method argument depending on the content type of the request. Optionally, automatic * validation can be applied by annotating the argument with {@code @Valid}. * * <p>Supported for annotated handler methods in Servlet environments. * * @author Arjen Poutsma * @since 3.0 * @see RequestHeader * @see ResponseBody * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestBody { /** * Whether body content is required. * <p>Default is {@code true}, leading to an exception thrown in case * there is no body content. Switch this to {@code false} if you prefer * {@code null} to be passed when the body content is {@code null}. * @since 3.2 */ boolean required() default true; }
required方法默认返回值是true。
这样问题就明朗了,我请求localhost的时候,没有传json过去,所以就会报400错误,因为客户端发送的请求在语法上是不正确的。
解决方法:在@RequestBody后面加上(required=false)就可以了。表示kehu对象可以不传入。
/** * 访问登录页 * @RequestBody(required=false) 表示kehu对象可以不传入。 * 一定要加上required=false,否则登录的时候会报400错误。错误代码: * The request sent by the client was syntactically incorrect. * @return * @throws Exception */ @RequestMapping(value = "/toLogin") public ModelAndView toLogin(HttpServletRequest request, @RequestBody(required=false) Kehu kehu) throws Exception { // 接收硬币机发送过来的json数据 if (kehu != null && !StringUtil.isEmpty(kehu.cmd)) { uploadData(kehu); } ModelAndView mv = new ModelAndView(); PageData pageData = this.getPageData(request); pageData.put("SYSNAME", Tools.readTxtFile(Const.SYSNAME)); // 读取系统名称 mv.setViewName("base/login"); mv.addObject("pd", pageData); return mv; }