众所周知,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;
}