`
m635674608
  • 浏览: 4932872 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Spring MVC 4.1 使用ResponseBodyAdvice支持jsonp

 
阅读更多

Spring MVC 4.1 使用ResponseBodyAdvice支持jsonp

使用ResponseBodyAdvice支持jsonp

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 方法的实现,

1
2
3
4
5
6
7
8
9
@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中实现的 ,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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 ,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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,具体的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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()方法真正写数据的时候了。看他是怎么写数据的,相关的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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);
   }
}
1
2
3
4
5
6
7
8
9
10
11
@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 + "(");
   }
}
1
2
3
4
5
6
7
8
@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调用的结果。

1
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=================

 

http://my.oschina.net/xinxingegeya/blog/480510?fromerr=yYIwo0JR

分享到:
评论

相关推荐

    Jsonp在spring MVC系统中的前后台交互源码实例

    Jsonp(JSON with Padding)是资料格式 json 的一种“使用模式”,可以让网页从别的网域获取资料。 本资料 是 spring MVC系统中用jsonp进行跨域解析。可实现前后台交互。

    Asp.net MVC3 实现JSONP

    Asp.net MVC3 实现JSONP

    Spring 4.1+JSONP的使用指南

    在解释JSONP之前,我们需要了解下”同源策略“,这对理解跨域有帮助。基于安全的原因,浏览器是存在同源策略机制的,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载额文档的属性。说的简单点就是浏览器...

    day17代码:springBoot整合JSONP

    day17代码:springBoot整合JSONP。Spring boot 实现json和jsonp格式数据,接口共用,Spring Boot支持JSONP跨域请求数据(Ajax的jsonp)。

    js跨域jsonp的使用

    jsonp的原理 jsonp的使用,使用jsonp解决js跨域问题!

    spring + jdbc框架

    这是一个自己搭建的spring mvc框架,使用jdbc连接关系型数据库,也可以使用非关系型数据库mongodb。 接口使用json数据格式通讯,也支持jsonp

    使用JSONP完成HTTP和HTTPS之间的跨域访问

    使用JSONP完成HTTP和HTTPS之间的跨域访问

    my jsonp with spring

    My jsonp 结合 spring 开发jsonp接口项目,具体demo参见: http://blog.csdn.net/xiuye2015/article/details/54375313 , 功能不全,只做练习.

    详解如何在Vue项目中发送jsonp请求

    起因 公司临时要支撑河南的一个项目,做一个单点登录的功能。 简单来说,就是以下3步 ...发送jsonp请求,axios官方貌似并不支持,所以排除:woman_gesturing_NO_light_skin_tone: 经过辗转Google,发现了*vue-json

    spring boot 支持js跨域请求

    spring boot 支持跨域 前台不需要jsonp 请求 正常js即可 spring boot 支持跨域 前台不需要jsonp 请求 正常js即可

    JSONP解决跨域问题

    前端使用jquery,datatype采用jsonp,服务端采用C#编写的webService

    JSONP简单调用实例

    JSONP简单调用实例。ASP.NET和纯HTML。jQuery的$.ajax的调用!jsonP说白了,就是在json字符串外面包上一个:参数名称+左右括弧!只是包了个:jsonpCallback() 而已! 相关文章:...

    jsonp 使用例子

    jsonp 使用例子,json 调用的从 alert 开始,到调用远程的jsonp服务,到实现自己的 jsonp 服务。

    spring boot 实践学习案例,与其它组件整合

    - Spring Boot AJAX 跨域,包括 JSONP、Node.js与SpringBoot集成使用反向代理 等。 - springboot-websockets - Spring Boot 使用 Websocket - springboot-webflux - Spring Boot 集成 WebFlux 开发反应式 Web...

    JSONP实现原理

    JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的...

    解决跨域封装的jsonp.js文件

    解决跨域封装的jsonp

    后台php设置jsonp

    后台php设置jsonp

    Jsonp和java操作

    Jsonp和java操作

    jsonp的demo

    简单使用百度接口,测试的jsonp的demo。简单使用百度接口,测试的jsonp的demo。 简单使用百度接口,测试的jsonp的demo。

Global site tag (gtag.js) - Google Analytics