文档章节

Spring MVC 4.1 支持jsonp

秋风醉了
 秋风醉了
发布于 2015/07/19 21:03
字数 1047
阅读 7202
收藏 7

Spring MVC 4.1 支持jsonp

使用ResponseBodyAdvice支持jsonp

ResponseBodyAdvice是一个接口,接口描述,

package org.springframework.web.servlet.mvc.method.annotation;

/**
 * Allows customizing the response after the execution of an {@code @ResponseBody}
 * or an {@code ResponseEntity} controller method but before the body is written
 * with an {@code HttpMessageConverter}.
 *
 * <p>Implementations may be may be registered directly with
 * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
 * or more likely annotated with {@code @ControllerAdvice} in which case they
 * will be auto-detected by both.
 *
 * @author Rossen Stoyanchev
 * @since 4.1
 */
public interface ResponseBodyAdvice<T> {

   /**
    * Whether this component supports the given controller method return type
    * and the selected {@code HttpMessageConverter} type.
    * @param returnType the return type
    * @param converterType the selected converter type
    * @return {@code true} if {@link #beforeBodyWrite} should be invoked, {@code false} otherwise
    */
   boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

   /**
    * Invoked after an {@code HttpMessageConverter} is selected and just before
    * its write method is invoked.
    * @param body the body to be written
    * @param returnType the return type of the controller method
    * @param selectedContentType the content type selected through content negotiation
    * @param selectedConverterType the converter type selected to write to the response
    * @param request the current request
    * @param response the current response
    * @return the body that was passed in or a modified, possibly new instance
    */
   T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
         Class<? extends HttpMessageConverter<?>> selectedConverterType,
         ServerHttpRequest request, ServerHttpResponse response);

}

作用:

Allows customizing the response after the execution of an {@code @ResponseBody} or an {@code ResponseEntity} controller method but before the body is written

with an {@code HttpMessageConverter}.

其中一个方法就是 beforeBodyWrite 在使用相应的HttpMessageConvert 进行write之前会被调用,就是一个切面方法。

和jsonp有关的实现类是AbstractJsonpResponseBodyAdvice,如下是 beforeBodyWrite 方法的实现,

@Override
public final Object beforeBodyWrite(Object body, MethodParameter returnType,
      MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType,
      ServerHttpRequest request, ServerHttpResponse response) {

   MappingJacksonValue container = getOrCreateContainer(body);
   beforeBodyWriteInternal(container, contentType, returnType, request, response);
   return container;
}

位于AbstractJsonpResponseBodyAdvice的父类中,而beforeBodyWriteInternal是在AbstractJsonpResponseBodyAdvice中实现的 ,如下,

@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
      MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {

   HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();

   for (String name : this.jsonpQueryParamNames) {
      String value = servletRequest.getParameter(name);
      if (value != null) {
         MediaType contentTypeToUse = getContentType(contentType, request, response);
         response.getHeaders().setContentType(contentTypeToUse);
         bodyContainer.setJsonpFunction(value);
         return;
      }
   }
}

就是根据callback 请求参数或配置的其他参数来确定返回jsonp协议的数据。

如何实现jsonp?

首先继承AbstractJsonpResponseBodyAdvice ,如下,

package com.usoft.web.controller.jsonp;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;

/**
 * 
 */
@ControllerAdvice(basePackages = "com.usoft.web.controller.jsonp")
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
    public JsonpAdvice() {
        super("callback", "jsonp");
    }
}

 super("callback", "jsonp");的意思就是当请求参数中包含callback 或 jsonp参数时,就会返回jsonp协议的数据。其value就作为回调函数的名称。

这里必须使用@ControllerAdvice注解标注该类,并且配置对哪些Controller起作用。关于注解@ControllerAdvice 的作用这里不做描述。

Controller实现jsonp,

package com.usoft.web.controller.jsonp;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.usoft.web.controller.JsonMapper;
import com.usoft.web.controller.Person;

/**
 * jsonp
 */
@Controller
public class JsonpController {

    /**
     * callback({"id":1,"age":12,"name":"lyx"})
     * 
     * @param args
     */
    public static void main(String args[]) {
        Person person = new Person(1, "lyx", 12);
        System.out.println(JsonMapper.nonNullMapper().toJsonP("callback",
            person));
    }

    @RequestMapping("/jsonp1")
    public Person jsonp1() {
        return new Person(1, "lyx", 12);
    }

    @RequestMapping("/jsonp2")
    @ResponseBody
    public Person jsonp2() {
        return new Person(1, "lyx", 12);
    }

    @RequestMapping("/jsonp3")
    @ResponseBody
    public String jsonp3() {
        return JsonMapper.nonNullMapper().toJsonP("callback",
            new Person(1, "lyx", 12));
    }
}

jsonp2 方法就是 一个jsonp协议的调用。http://localhost:8081/jsonp2?callback=test可以直接调用这个方法,并且返回jsonp协议的数据。

通过debug代码,我们来看一下他是怎么返回jsonp协议的数据的。

正因为我们前面在 该Controller 上配置了 JsonpAdvice 的 ControllerAdvice,在调用 MappingJackson2HttpMessageConverter的write()方法往回写数据的时候,首先会调用

beforeBodyWrite,具体的代码如下,

@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
      MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {

   HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();

   for (String name : this.jsonpQueryParamNames) {
      String value = servletRequest.getParameter(name);
      if (value != null) {
         MediaType contentTypeToUse = getContentType(contentType, request, response);
         response.getHeaders().setContentType(contentTypeToUse);
         bodyContainer.setJsonpFunction(value);
         return;
      }
   }
}

当请求参数中含有配置的相应的回调参数时,就会bodyContainer.setJsonpFunction(value);这就标志着 返回的数据时jsonp格式的数据。

然后接下来就到了 MappingJackson2HttpMessageConverter 的write()方法真正写数据的时候了。看他是怎么写数据的,相关的代码如下,

@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {

   JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
   JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
   try {
      writePrefix(generator, object);
      Class<?> serializationView = null;
      Object value = object;
      if (value instanceof MappingJacksonValue) {
         MappingJacksonValue container = (MappingJacksonValue) object;
         value = container.getValue();
         serializationView = container.getSerializationView();
      }
      if (serializationView != null) {
         this.objectMapper.writerWithView(serializationView).writeValue(generator, value);
      }
      else {
         this.objectMapper.writeValue(generator, value);
      }
      writeSuffix(generator, object);
      generator.flush();

   }
   catch (JsonProcessingException ex) {
      throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex);
   }
}
@Override
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
   if (this.jsonPrefix != null) {
      generator.writeRaw(this.jsonPrefix);
   }
   String jsonpFunction =
         (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);
   if (jsonpFunction != null) {
      generator.writeRaw(jsonpFunction + "(");
   }
}
@Override
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
   String jsonpFunction =
         (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null);
   if (jsonpFunction != null) {
      generator.writeRaw(");");
   }
}

代码非常清晰。看我们jsonp调用的结果。

http://localhost:8081/jsonp2?callback=test

响应消息如下,

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: application/javascript

Transfer-Encoding: chunked

Date: Sun, 19 Jul 2015 13:01:02 GMT


test({"id":1,"age":12,"name":"lyx"});

=================END=================

© 著作权归作者所有

秋风醉了
粉丝 247
博文 542
码字总数 412294
作品 0
朝阳
程序员
私信 提问
加载中

评论(7)

顾咩咩
中文乱码问题怎么处理
御座大大
御座大大
有源码么
赵文斌1
springmvc的配置文件需要配置什么吗?我试用这方法没有成功?
乔码侠
乔码侠
写的什么鬼,就不能用点心、
蓝奔z
蓝奔z
起码我对着程序能跳到服务里面去,犀利犀利~
中小宇
中小宇
不错,学习到了
Spring4.1新特性——Spring MVC增强

1、GroovyWebApplicationContext 在Spring 4.1之前没有提供Web集成的ApplicationContext,在《Spring4新特性——Groovy Bean定义DSL》中我们自己去实现的com.sishuok.spring4.context.suppo...

莱茵河水怪v241Beta
2015/07/23
0
0
Spring Boot学习资源

Spring Boot 入门 http://blog.csdn.net/isea533/article/details/50278205 ---------------------------------------------------------- Spring Boot 静态资源处理 http://blog.csdn.net/c......

IamOkay
2017/02/17
0
0
Spring 3.0.4 发布,支持Ehcache 2.2 等

Spring 刚刚发布了 3.0.4 版本,该版本修复了80个bug,主要改进内容包括更新对一些第三方包的支持,包括:Hibernate Core 3.6, Hibernate Validator 4.1, EclipseLink 2.1, and Ehcache 2.2...

红薯
2010/08/20
3.6K
0
搞定所有的跨域请求问题 : jsonp & CORS

原文出处:JavaDoop 网上各种跨域教程,各种实践,各种问答,除了简单的 jsonp 以外,很多说 CORS 的都是行不通的,老是缺那么一两个关键的配置。本文只想解决问题,所有的代码经过亲自实践。...

JavaDoop
2017/11/30
0
0
Spring Framework 4.1 GA 发布

Spring Framework 4.1 GA 发布,此版本更新内容如下: 新特性和新主题: 注解方式的 JMS 侦听器方法 完全支持 JCache (JSR-107) 注解 灵活的解析和转换静态 Web 资源 MVC 视图: 声明式解析、...

oschina
2014/09/05
7.2K
32

没有更多内容

加载失败,请刷新页面

加载更多

将博客搬至CSDN

https://blog.csdn.net/qq_38157006

Marhal
18分钟前
1
0
unicode Java中求字符串长度length()和codePointCount()的区别

在计算字符串长度时,Java的两种方法length()和codePointCount()一度让我困惑,运行书上例子得到的长度值是相等的,那为什么要设定两个方法呢? 对于普通字符串,这两种方法得到的值是一样的...

泉天下
18分钟前
2
0
uin-app 一、学习理由

选择uni-app 理由 别人的理由 1. 5+ 有HTML5+和Native.js技术,HTML5+包含常用的跨平台的几百个API,能满足常规开发需求,而Native.js把40w原生api映 射成js对象,这样js可以直接调原生。HTM...

轻轻的往前走
20分钟前
1
0
方括号及其在命令行中的不同用法介绍

通配 方括号最简单的用法就是通配。你可能在知道“ Globbing”这个概念之前就已经通过通配来匹配内容了,列出具有相同特征的多个文件就是一个很常见的场景,例如列出所有 JPEG 文件: ls *.j...

Linux就该这么学
26分钟前
2
0
vecty 基础

gopherjs 是把 go 编译为 js 的工具。 vecty 是基于 gopherjs 的一种类似 React 的开发框架。 安装 gopherjs 和 vecty go get -u github.com/gopherjs/gopherjsgo get -u github.com/gopher......

electricface
27分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部