@RequestBody與Content-type

1、簡介

Spring Web MVC 是一種基于Java的實現(xiàn)了Web MVC設(shè)計模式的請求驅(qū)動類型的輕量級Web框架,自Spring MVC出現(xiàn)以來,Java服務(wù)端開發(fā)逐漸采用Spring MVC編寫Http接口。今天主要跟大家分享一個 @RequestBodyapplication/x-www-form-urlencoded 同時使用時遇到的坑。

2、問題描述

要發(fā)送的HTTP請求:

curl --header 'content-type:application/x-www-form-urlencoded;charset=UTF-8' -d 'msgObject=string&msgTitle=string&msgContent=string&msgLevel=string&msgSource=string&msgSenders=string&msgReceivers=string' 'http://127.0.0.1:8080/service/send'

服務(wù)端Controller:

@RequestMapping(value = "/alarm/send", method = RequestMethod.POST)
public ResponseEntity<String> sendAlarm1(@RequestBody MessageAlarmReq req) {
    boolean success = false;
    System.out.println(req);
    return new ResponseEntity<String>(HttpStatus.OK);
}

其中,MessageAlarmReq定義:

@Data
public class MessageAlarmReq {
    private String msgObject;
    private String msgTitle;
    private String msgContent;
    private String msgLevel;
    private String msgSource;
    private String msgSenders;
    private String msgReceivers;
}

本希望Spring MVC將body映射為MessageAlarmReq對象,但實際上得到了:

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded' not supported

3、問題分析

Spring 3.X系列增加了新注解 @ResponseBody,@RequestBody。
@RequestBody 將HTTP請求正文轉(zhuǎn)換為適合的HttpMessageConverter對象。
Spring MVC 使用HttpMessageConverter將請求對象轉(zhuǎn)化為我們希望的格式,當(dāng)我們在方法中加入@RequestBody注解,Spring MVC固定使用RequestMappingHandlerAdapter來解析,其中RequestMappingHandlerAdapter支持的HttpMessageConverter有四種:

ByteArrayHttpMessageConverter:轉(zhuǎn)化 byte arrays.
StringHttpMessageConverter:轉(zhuǎn)化 Strings.
FormHttpMessageConverter:轉(zhuǎn)化 form data to/from a MultiValueMap.
SourceHttpMessageConverter:轉(zhuǎn)化 to/from a javax.xml.transform.Source.

通過分析這四個Converter可知,FormHttpMessageConverter支持解析application/x-www-form-urlencoded這種格式:

private List<MediaType> supportedMediaTypes = new ArrayList();
this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
this.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);

其中:
MediaType APPLICATION_FORM_URLENCODED = valueOf("application/x-www-form-urlencoded");
MediaType MULTIPART_FORM_DATA = valueOf("multipart/form-data");

了解HttpMessageConverter原理可知,HttpMessageConverter使用:

T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

將參數(shù)轉(zhuǎn)化為我們希望的對象,FormHttpMessageConverter的實現(xiàn)方式為:

public MultiValueMap<String, String> read(@Nullable Class<? extends MultiValueMap<String, ?>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException{ ...... };

由此,我們可以知道,F(xiàn)ormHttpMessageConverter只能將requestBody轉(zhuǎn)化為MultiValueMap,而不能是自定義對象。

4、解決方案

方案一、

將 @RequestBody 參數(shù)設(shè)置為 MultiValueMap,即 Controller定義修改為:

@RequestMapping(value = "/alarm/send", method = RequestMethod.POST)
public ResponseEntity<String> sendAlarm1(@RequestBody MultiValueMap<String, String> req) {
    boolean success = false;
    System.out.println(req);
    return new ResponseEntity<String>(HttpStatus.OK);
}

這樣就能符合FormHttpMessageConverter的要求。

方案二、

使用ModelAttribute:

@RequestMapping(value = "/alarm/send", method = RequestMethod.POST)
public ResponseEntity<String> sendAlarm1(@ModelAttribute("alarmReq") MessageAlarmReq req)
 { ...... }

@ModelAttribute("alarmReq")
public MessageAlarmReq getMessageAlarmReq() {
    return new MessageAlarmReq();
}

利用ModelAttribute進(jìn)行對象映射,參考文獻(xiàn)

5、相關(guān)問題

經(jīng)過測試我發(fā)現(xiàn)假如代碼中直接刪除@RequestBody,也可以實現(xiàn) RequestBody 直接映射為自定義對象:

@RequestMapping(value = "/alarm/send", method = RequestMethod.POST)
public ResponseEntity<String> sendAlarm1(MessageAlarmReq req) {
    boolean success = false;
    System.out.println(req);
    return new ResponseEntity<String>(HttpStatus.OK);
}

此處暫時還沒找到具體原因,因為項目用的是Spring Boot 2.0,推測有可能是Spring Boot做的一些優(yōu)化。

6、總結(jié)

通過上述分析,我們看到在我們使用@RequestBody,享受其便利,增大方法可讀性時,無形中也受到RequestMappingHandlerAdapter中HttpMessageConverter的限制,對此個人建議:

  1. 針對新編寫的接口,優(yōu)先使用@RequestBody,前后端傳參時盡量使用json格式,增加代碼可讀性;
  2. 為兼容老版本接口,不要再使用@RequestBody,推薦使用@ModelAttribute,充分利用Spring MVC自帶Converter的參數(shù)映射,減少代碼邏輯;
  3. 如果項目中有很多Content-Type:application/x-www-form-urlencoded的情況,推薦擴(kuò)展HttpMessageConverter,自己來編碼處理對象映射;
  4. 不推薦 5 中描述的直接刪除@RequestBody的方式,因為目前具體原因不明,不排除未來有其他坑的可能。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,279評論 6 342
  • Spring的模型-視圖-控制器(MVC)框架是圍繞一個DispatcherServlet來設(shè)計的,這個Servl...
    alexpdh閱讀 2,746評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,641評論 19 139
  • 翻譯自Spring官方文檔 4.1.2版本 相關(guān)文章: Spring參考手冊 1 Spring Framework...
    liycode閱讀 777評論 0 2
  • 現(xiàn)在想來,生活在某些方面確實是公平的。我們經(jīng)常怨天尤人,抱怨自我不幸, 卻并未察覺在不幸的另一面竟然是大大的萬幸...
    給你一嘴巴丫子的喬治閱讀 1,346評論 0 1

友情鏈接更多精彩內(nèi)容