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