簡介:
@RequestBody
作用:
i) 該注解用於讀取Request請求的body部分數據,使用系統默認配置的HttpMessageConverter進行解析,然后把相應的數據綁定到要返回的對象上;
ii) 再把HttpMessageConverter返回的對象數據綁定到 controller中方法的參數上。
使用時機:
A) GET、POST方式提時, 根據request header Content-Type的值來判斷:
- application/x-www-form-urlencoded, 可選(即非必須,因為這種情況的數據@RequestParam, @ModelAttribute也可以處理,當然@RequestBody也能處理);
- multipart/form-data, 不能處理(即使用@RequestBody不能處理這種格式的數據);
- 其他格式, 必須(其他格式包括application/json, application/xml等。這些格式的數據,必須使用@RequestBody來處理);
B) PUT方式提交時, 根據request header Content-Type的值來判斷:
- application/x-www-form-urlencoded, 必須;
- multipart/form-data, 不能處理;
- 其他格式, 必須;
說明:request的body部分的數據編碼格式由header部分的Content-Type指定;
@ResponseBody
作用:
該注解用於將Controller的方法返回的對象,通過適當的HttpMessageConverter轉換為指定格式后,寫入到Response對象的body數據區。
使用時機:
返回的數據不是html標簽的頁面,而是其他某種格式的數據時(如json、xml等)使用;
HttpMessageConverter(HTTP消息轉化器)
- * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
- *
- * @author Arjen Poutsma
- * @author Juergen Hoeller
- * @since 3.0
- */
- public interface HttpMessageConverter<T> {
- /**
- * Indicates whether the given class can be read by this converter.
- * @param clazz the class to test for readability
- * @param mediaType the media type to read, can be {@code null} if not specified.
- * Typically the value of a {@code Content-Type} header.
- * @return {@code true} if readable; {@code false} otherwise
- */
- boolean canRead(Class<?> clazz, MediaType mediaType);
- /**
- * Indicates whether the given class can be written by this converter.
- * @param clazz the class to test for writability
- * @param mediaType the media type to write, can be {@code null} if not specified.
- * Typically the value of an {@code Accept} header.
- * @return {@code true} if writable; {@code false} otherwise
- */
- boolean canWrite(Class<?> clazz, MediaType mediaType);
- /**
- * Return the list of {@link MediaType} objects supported by this converter.
- * @return the list of supported media types
- */
- List<MediaType> getSupportedMediaTypes();
- /**
- * Read an object of the given type form the given input message, and returns it.
- * @param clazz the type of object to return. This type must have previously been passed to the
- * {@link #canRead canRead} method of this interface, which must have returned {@code true}.
- * @param inputMessage the HTTP input message to read from
- * @return the converted object
- * @throws IOException in case of I/O errors
- * @throws HttpMessageNotReadableException in case of conversion errors
- */
- T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
- throws IOException, HttpMessageNotReadableException;
- /**
- * Write an given object to the given output message.
- * @param t the object to write to the output message. The type of this object must have previously been
- * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
- * @param contentType the content type to use when writing. May be {@code null} to indicate that the
- * default content type of the converter must be used. If not {@code null}, this media type must have
- * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
- * returned {@code true}.
- * @param outputMessage the message to write to
- * @throws IOException in case of I/O errors
- * @throws HttpMessageNotWritableException in case of conversion errors
- */
- void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
- throws IOException, HttpMessageNotWritableException;
- }
該接口定義了四個方法,分別是讀取數據時的 canRead(), read() 和 寫入數據時的canWrite(), write()方法。
在使用 <mvc:annotation-driven />標簽配置時,默認配置了RequestMappingHandlerAdapter(注意是RequestMappingHandlerAdapter不是AnnotationMethodHandlerAdapter,詳情查看Spring 3.1 document “16.14 Configuring Spring MVC”章節),並為他配置了一下默認的HttpMessageConverter:
- ByteArrayHttpMessageConverter converts byte arrays.
- StringHttpMessageConverter converts strings.
- ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
- SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
- FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
- Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present on the classpath.
- MappingJacksonHttpMessageConverter converts to/from JSON — added if Jackson is present on the classpath.
- AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
- RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.
- ByteArrayHttpMessageConverter: 負責讀取二進制格式的數據和寫出二進制格式的數據;
- StringHttpMessageConverter: 負責讀取字符串格式的數據和寫出二進制格式的數據;
- ResourceHttpMessageConverter:負責讀取資源文件和寫出資源文件數據;
- FormHttpMessageConverter: 負責讀取form提交的數據(能讀取的數據格式為 application/x-www-form-urlencoded,不能讀取multipart/form-data格式數據);負責寫入application/x-www-from-urlencoded和multipart/form-data格式的數據;
- MappingJacksonHttpMessageConverter: 負責讀取和寫入json格式的數據;
- SouceHttpMessageConverter: 負責讀取和寫入 xml 中javax.xml.transform.Source定義的數據;
- Jaxb2RootElementHttpMessageConverter: 負責讀取和寫入xml 標簽格式的數據;
- AtomFeedHttpMessageConverter: 負責讀取和寫入Atom格式的數據;
- RssChannelHttpMessageConverter: 負責讀取和寫入RSS格式的數據;
- 當使用@RequestBody和@ResponseBody注解時,RequestMappingHandlerAdapter就使用它們來進行讀取或者寫入相應格式的數據
HttpMessageConverter匹配過程:
@RequestBody注解時: 根據Request對象header部分的Content-Type類型,逐一匹配合適的HttpMessageConverter來讀取數據;
spring 3.1源代碼如下:
- private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
- throws Exception {
- MediaType contentType = inputMessage.getHeaders().getContentType();
- if (contentType == null) {
- StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
- String paramName = methodParam.getParameterName();
- if (paramName != null) {
- builder.append(' ');
- builder.append(paramName);
- }
- throw new HttpMediaTypeNotSupportedException(
- "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
- }
- List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
- if (this.messageConverters != null) {
- for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
- allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
- if (messageConverter.canRead(paramType, contentType)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
- +"\" using [" + messageConverter + "]");
- }
- return messageConverter.read(paramType, inputMessage);
- }
- }
- }
- throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
- }
@ResponseBody注解時: 根據Request對象header部分的Accept屬性(逗號分隔),逐一按accept中的類型,去遍歷找到能處理的HttpMessageConverter;
源代碼如下:
- private void writeWithMessageConverters(Object returnValue,
- HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
- throws IOException, HttpMediaTypeNotAcceptableException {
- List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
- if (acceptedMediaTypes.isEmpty()) {
- acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
- }
- MediaType.sortByQualityValue(acceptedMediaTypes);
- Class<?> returnValueType = returnValue.getClass();
- List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
- if (getMessageConverters() != null) {
- for (MediaType acceptedMediaType : acceptedMediaTypes) {
- for (HttpMessageConverter messageConverter : getMessageConverters()) {
- if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
- messageConverter.write(returnValue, acceptedMediaType, outputMessage);
- if (logger.isDebugEnabled()) {
- MediaType contentType = outputMessage.getHeaders().getContentType();
- if (contentType == null) {
- contentType = acceptedMediaType;
- }
- logger.debug("Written [" + returnValue + "] as \"" + contentType +
- "\" using [" + messageConverter + "]");
- }
- this.responseArgumentUsed = true;
- return;
- }
- }
- }
- for (HttpMessageConverter messageConverter : messageConverters) {
- allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
- }
- }
- throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
- }
補充:
MappingJacksonHttpMessageConverter 調用了 objectMapper.writeValue(OutputStream stream, Object)方法,使用@ResponseBody注解返回的對象就傳入Object參數內。若返回的對象為已經格式化好的json串時,不使用@RequestBody注解,而應該這樣處理:
1、response.setContentType("application/json; charset=UTF-8");
2、response.getWriter().print(jsonStr);
直接輸出到body區,然后的視圖為void。
參考資料:
1、 Spring 3.1 Doc:
spring-3.1.0/docs/spring-framework-reference/html/mvc.html
2、Spring 3.x MVC 入門4 -- @ResponseBody & @RequestBody
轉載地址:http://www.cnblogs.com/qq78292959/p/3760651.html
ok 上面講解了實現的過程,簡單的可以理解為只要有對應協議的解析器,你就可以通過幾行配置,幾個注解完成協議——對象的轉換工作。
注意:Spring默認的json協議解析由Jackson完成,所以要添加jackson.jar包。
使用@ResponseBody 帶來的好處就是由spring 將協議轉化為對象,不用我們自己再去轉化 同樣@RequestBody也是,比如我們在前台傳遞了參數而controller中使用@RequestBody 則可以直接獲得pojo對象,不用我們再去獲取參數,組裝對象。
下面看看小demo
1、前端代碼post提交:
- function profilep() {
- // 組裝json格式數據
- var mydata = '{"name":"' + $('#name').val() + '","id":"'
- + $('#id').val() + '","password":"' + $('#password').val()
- + '"}';
- $.ajaxSetup({
- contentType : 'application/json'
- });
- $.post('http://localhost:18080/website2/user/save2.do', mydata,
- function(data) {
- alert("id: " + data.id + "\nname: " + data.name
- + "\password: " + data.password);
- }, 'json');
- }
2、后台代碼:
- @ResponseBody
- @RequestMapping(value = "/save2.do" ,method = RequestMethod.POST)
- // 知己接收對象,因@RequestBody spring 幫我們處理了 協議到對象的這個過程
- public User info2(@RequestBody User user) {
- String id = user.getId();
- String name = user.getName();
- String password = user.getPassword();
- Map<String, String> map = new HashMap<String, String>();
- map.put("id", id);
- map.put("name", name);
- map.put("password", password);
- userService.saveUser(map);
- User user2= new User(id,name,password);
- // 直接返回對象,因@ResponseBody spring 會幫我們處理對象和協議之間的轉化
- return user2;
- }
3、上面兩個都是post方法其實get方法也是一樣的:
前端代碼:
- function profile() {
- var url = 'http://localhost:18080/website2/user/save.do?';
- var query = 'id=' + $('#id').val() + '&name=' + $('#name').val()
- + '&password=' + $('#password').val();
- url += query;
- $.get(url, function(data) {
- alert("id: " + data.id + "\nname: " + data.name + "\nstatus: "
- + data.password);
- });
- }
后台代碼:
- @ResponseBody
- @RequestMapping(value = "/save.do" ,method = RequestMethod.GET)
- public User info(HttpServletRequest request) {
- String id = request.getParameter("id");
- String name = request.getParameter("name");
- String password = request.getParameter("password");
- Map<String, String> map = new HashMap<String, String>();
- map.put("id", id);
- map.put("name", name);
- map.put("password", password);
- userService.saveUser(map);
- User user= new User(id,name,password);
- return user;
- }
上面的$.get方法是使用GET方式進行異步請求。$.post方法使用POST方式來進行異步請求。
$.get 、$.post 方法的格式相同,這里就以$.get為列:
$.get(url [, data] [, callback] [, type])
url:請求的HTML頁的URL地址
data(可選):發送至服務器的key/value數據會未為QueryString附加到請求URL中
callback(可選):載入成功時回調函數(即當Response的返回狀態為success才調用)自動將請求結果和狀態傳遞給該方法
type(可選):服務器端返回內容的格式,包括xml,html,script,json,text和_default.
這個格式通過上面的post方法和get方法可以看到,像post的那個方法就比較完整!
