springMvc 之 @ResponseBody 前后端联调!

应用场景:前后端对接调试!

问题说明:在前后端对接的时候,使用注解  @ResponseBody 返回去前端json数据格式,但是问题就出现在这,页面返回406,406状态很比较少见了.查了下大概意思是

请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。

简单的说就是你的http请求的 content-Type不对,打开调试台看下! 正常情况是这个样子的,406 出现的是 红色情况!

 

解决办法:网上有很多情况,说明个解决办法 这里只说下我自己的情况:通常情况下,在整合springMvc时候.配置文件都会配置一个视图解析器,这个时候就要看下springmvc的执行流程了说起了;

1.DispatcherServlet 接收到Request请求

2.HandlerMapping选择一个适合的Handler处理Request请求!

3-4.选择合适的HandlerAdapter,调用用户编写的Controller处理业务逻辑。(HandlerAdapter主要是帮助Spring MVC支持多种类型的Controller)

5.Controller将返回结果放置到Model中并且返回view名称给Handler Adapter

6.DispatcherServlet选择合适的ViewResolver来生成View对象

7-8. View对象利用Model中的数据进行渲染并返回数据

代码流程:

从上面的流程图我们可以看到,content-type header是单独被处理,具体过程可以参考下面的源码(AbstractMessageConverterMethodProcessor):

protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException {
 
    Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
    HttpServletRequest servletRequest = inputMessage.getServletRequest();
    List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest); //适合的兼容media types类型实际上,我们可以使用produces = {}来指定我们需要的mediatype
    List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);
 
    Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
    for (MediaType requestedType : requestedMediaTypes) {
        for (MediaType producibleType : producibleMediaTypes) {
            if (requestedType.isCompatibleWith(producibleType)) {
                compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
            }
        }
    }
    if (compatibleMediaTypes.isEmpty()) {
        if (returnValue != null) {
            throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
        }
        return;
    }
 
    List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
    MediaType.sortBySpecificityAndQuality(mediaTypes);
 
    MediaType selectedMediaType = null;   //选择最匹配的mediaType
    for (MediaType mediaType : mediaTypes) {
        if (mediaType.isConcrete()) {
            selectedMediaType = mediaType;
            break;
        }
        else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
            selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
            break;
        }
    }
 
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> messageConverter : this.messageConverters) {         //遍历messageConvertors, 寻找可以处理相应返回类型和mediatype的HttpMessageConvertor
            if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
                returnValue = this.adviceChain.invoke(returnValue, returnType, selectedMediaType,
                        (Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                if (returnValue != null) {         //这里将会填充mediatype到header,并将httpmessage发送给请求者
                    ((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
                                messageConverter + "]");
                    }
                }
                return;
            }
        }
    }
 
    if (returnValue != null) {
        throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
    }
}

接下来,将选择好的mediatype写入到HttpOutputMessage中:

ublic final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {
 
    final HttpHeaders headers = outputMessage.getHeaders();     //设置contenttype到HttpOutputMessage
    if (headers.getContentType() == null) {
        MediaType contentTypeToUse = contentType;
        if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
            contentTypeToUse = getDefaultContentType(t);
        }
        if (contentTypeToUse != null) {
            headers.setContentType(contentTypeToUse);
        }
    }
    if (headers.getContentLength() == -1) {
        Long contentLength = getContentLength(t, headers.getContentType());
        if (contentLength != null) {
            headers.setContentLength(contentLength);
        }
    }
      /* 省略了不相干代码 */
}

最终的Headers设置在ServletServerHttpResponse类中完成,

private void writeHeaders() {
    if (!this.headersWritten) {
        for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
            String headerName = entry.getKey();
            for (String headerValue : entry.getValue()) {         //将复合类中之前设置的header(content-type)内容补充到servletResponse
                this.servletResponse.addHeader(headerName, headerValue);
            }
        }
        // HttpServletResponse exposes some headers as properties: we should include those if not already present
        if (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) {
            this.servletResponse.setContentType(this.headers.getContentType().toString());
        }
        if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null &&
                this.headers.getContentType().getCharSet() != null) {
            this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name());
        }
        this.headersWritten = true;
    }
}

从上述的代码中,我们可以看到在RequestResponseBodyMethodProcessor这个ReturnValueHandler中,media-type被单独的逻辑进行处理,因此直接在ServletResponse中设置content-type header并不能正常生效。

需要在@RequestMapping中添加produces = {} 进行设置,也可以在配置文件中添加配置文件!:


<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
		<property name="messageConverters">
			<list >
				<ref bean="mappingJacksonHttpMessageConverter" />
			</list>
		</property>
	</bean>

<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
		<property name="supportedMediaTypes">
			<list>
				<value>application/json;charset=UTF-8</value>
				<value>text/html;charset=UTF-8</value>
			</list>
		</property>
</bean>

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值