@RequestBody和@ResponseBody

        给接口写假数据的时候,为了返回到前台是一个JSON串,所以加了@ResponseBody注解.但是这个注解我以前一直没有用过.所以和@RequestBody一起了解下.

        @RequestBody,这个注解用于读取Request请求的body部分数据,使用系统默认的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;然后再把HttpMessageConverter返回的对象数据绑定到controller中方法的参数上.

         若是GET,POST方式提交时,根据request的header中的Content-Type的值来判断(说明一下,request请求中的body部分的数据编码格式是由header部分的Content-Type制定的.).

        如果是application/x-www-form-urlencoded,则是否使用@RequestBody时为可选,也可以用@RequestParam,@ModelAttribute来处理.若是multipart/form-data,@RequestBody不能处理这种格式的数据;而其他的格式,必须使用@RequestBody来处理,包括application/json,application/xml.

         若是PUT方式提交的,也是根据Content-Type的值来判断.application/x-www-form-urlencoded这回是必须用@RequestBody来处理,而multipart/form-data,则同样还是不能处理;其他,也同样还是必须用@RequestBody来处理.

        而@ResponseBody,则是用于将controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区.通常在返回的数据不是html标签的页面,而是其他某个格式的数据(如json,xml等)时使用.

          查看HttpMessageConvert的源码.

public interface HttpMessageConverter<T> {
 
	boolean canRead(Class<?> clazz, MediaType mediaType);
	boolean canWrite(Class<?> clazz, MediaType mediaType);
	List<MediaType> getSupportedMediaTypes();
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}

        这是一个接口,该接口中定义了5个方法,除了getSupportedMediaTypes方法,其他4个分别是读取数据时的canRead(),read()和写入数据时的canWrite(),write()方法.   

       那HttpMessageConvert是在什么时候配置的?

       在使用<MVC:annotation-driven />注解驱动标签配置时,默认配置了RequestMappingHandlerAdapter,并为他配置了一下默认的HttpMessageConverter. 如下是提供的九种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,用于读写资源文件;

        SourceHttpMessageConverter,用于读写xml中的javax.xml.transform.Source定义的数据;

        FormHttpMessageConverter,用于读form提交的数据.能读取application/x-www-form-urlencoded,不能读取multipart/form-data格式数据;以及写入application/x-www-form-urlencoded和multipart/form-data格式的数据;

        Jaxb2RootElementHttpMessageConverter,用于读写xml标签格式的数据;

        MappingJacksonHttpMessageConverter,用于读写json格式的数据;

         AtomFeedHttpMessageConverter,用于读写Atom格式的数据;

         RssChannelHttpMessageConverter,用于读写Rss格式的数据.

         当我们使用@RequestBody或@ResponseBody注解时,RequestMappingHandlerAdapter请求对应的适配器就根据情况,使用上述的9种转换器来读取或者写入相应格式的数据.

         而HttpMessageConverter匹配的过程,对于@RequestBody注解,就是根据Request对象的Content-Type类型,来逐一匹配合适的HttpMessageConverter,然后读取Request中的body数据.

         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);  
    }

         方法为readWithMessageConverter,用消息转换器读,参数为MethodParamter方法的参数,HttpInputMessage,输入消息,以及paramType,参数的class类型.并抛出Exception异常.

        首先获取到输入参数中的header部分的Content-Type,这是一个MediaType类型的值.若该值为null,则抛异常.若有值,则继续执行.遍历所有的messageConverters,若根据参数类型和content-type能够读取,则读取输入消息中的内容.即匹配成功.

         而@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);  
        }
        wirteWithMessageConverters,用MessageConverters写入,参数为Object类型的returnValue,HttpInputMessage类型的输入消息,以及HttpOutputMessage的输出消息,抛出IO异常,Media类型不被接受异常.

        首先获取输入消息的header的accept,这是一个MediaType类型的list集合.然后对该list集合进行排序.然后遍历list集合,并遍历所有的MessageConverter,若当前的MessageConverter可以配合returnValue的类型,写入集合中的当前记录,并写入到输出消息中,则匹配成功.

        其他,MappingJacksonHttpMessageConverter调用了objectMapper.writeValue(OutputStream stream,Object obj)方法,使用@ResponseBody注解返回的对象就传入到Object参数内.若返回的对象为已经格式化的json串时,不使用@ResponseBody注解,而应该这样处理:

       1.response.setContentType("application/json;charset=UTF-8");

       2.response.getWriter().print(jsonSr);

       直接输出到body去,然后就不需要返回String字符串作为视图了,返回值为void.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值