Spring MVC 4.1 支持jsonp

使用ResponseBodyAdvice支持jsonp

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

1package org.springframework.web.servlet.mvc.method.annotation; 2 3/** 4 * Allows customizing the response after the execution of an {@code @ResponseBody} 5 * or an {@code ResponseEntity} controller method but before the body is written 6 * with an {@code HttpMessageConverter}. 7 * 8 * <p>Implementations may be may be registered directly with 9 * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver} 10 * or more likely annotated with {@code @ControllerAdvice} in which case they 11 * will be auto-detected by both. 12 * 13 * @author Rossen Stoyanchev 14 * @since 4.1 15 */ 16public interface ResponseBodyAdvice<T> { 17 18   /** 19    * Whether this component supports the given controller method return type 20    * and the selected {@code HttpMessageConverter} type. 21    * @param returnType the return type 22    * @param converterType the selected converter type 23    * @return {@code true} if {@link #beforeBodyWrite} should be invoked, {@code false} otherwise 24    */ 25   boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); 26 27   /** 28    * Invoked after an {@code HttpMessageConverter} is selected and just before 29    * its write method is invoked. 30    * @param body the body to be written 31    * @param returnType the return type of the controller method 32    * @param selectedContentType the content type selected through content negotiation 33    * @param selectedConverterType the converter type selected to write to the response 34    * @param request the current request 35    * @param response the current response 36    * @return the body that was passed in or a modified, possibly new instance 37    */ 38   T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType, 39         Class<? extends HttpMessageConverter<?>> selectedConverterType, 40         ServerHttpRequest request, ServerHttpResponse response); 41 42}

作用:

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@Override 2public final Object beforeBodyWrite(Object body, MethodParameter returnType, 3      MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType, 4      ServerHttpRequest request, ServerHttpResponse response) { 5 6   MappingJacksonValue container = getOrCreateContainer(body); 7   beforeBodyWriteInternal(container, contentType, returnType, request, response); 8   return container; 9}

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

1@Override 2protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, 3      MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) { 4 5   HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); 6 7   for (String name : this.jsonpQueryParamNames) { 8      String value = servletRequest.getParameter(name); 9      if (value != null) { 10         MediaType contentTypeToUse = getContentType(contentType, request, response); 11         response.getHeaders().setContentType(contentTypeToUse); 12         bodyContainer.setJsonpFunction(value); 13         return; 14      } 15   } 16}

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

如何实现jsonp?

首先继承AbstractJsonpResponseBodyAdvice ,如下,

1package com.usoft.web.controller.jsonp; 2 3import org.springframework.web.bind.annotation.ControllerAdvice; 4import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; 5 6/** 7 *  8 */ 9@ControllerAdvice(basePackages = "com.usoft.web.controller.jsonp") 10public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { 11    public JsonpAdvice() { 12        super("callback", "jsonp"); 13    } 14}

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

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

Controller实现jsonp,

1package com.usoft.web.controller.jsonp; 2 3import org.springframework.stereotype.Controller; 4import org.springframework.web.bind.annotation.RequestMapping; 5import org.springframework.web.bind.annotation.ResponseBody; 6 7import com.usoft.web.controller.JsonMapper; 8import com.usoft.web.controller.Person; 9 10/** 11 * jsonp 12 */ 13@Controller 14public class JsonpController { 15 16    /** 17     * callback({"id":1,"age":12,"name":"lyx"}) 18     *  19     * @param args 20     */ 21    public static void main(String args[]) { 22        Person person = new Person(1, "lyx", 12); 23        System.out.println(JsonMapper.nonNullMapper().toJsonP("callback", 24            person)); 25    } 26 27    @RequestMapping("/jsonp1") 28    public Person jsonp1() { 29        return new Person(1, "lyx", 12); 30    } 31 32    @RequestMapping("/jsonp2") 33    @ResponseBody 34    public Person jsonp2() { 35        return new Person(1, "lyx", 12); 36    } 37 38    @RequestMapping("/jsonp3") 39    @ResponseBody 40    public String jsonp3() { 41        return JsonMapper.nonNullMapper().toJsonP("callback", 42            new Person(1, "lyx", 12)); 43    } 44}

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

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

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

beforeBodyWrite,具体的代码如下,

1@Override 2protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, 3      MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) { 4 5   HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); 6 7   for (String name : this.jsonpQueryParamNames) { 8      String value = servletRequest.getParameter(name); 9      if (value != null) { 10         MediaType contentTypeToUse = getContentType(contentType, request, response); 11         response.getHeaders().setContentType(contentTypeToUse); 12         bodyContainer.setJsonpFunction(value); 13         return; 14      } 15   } 16}

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

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

1@Override 2protected void writeInternal(Object object, HttpOutputMessage outputMessage) 3      throws IOException, HttpMessageNotWritableException { 4 5   JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); 6   JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); 7   try { 8      writePrefix(generator, object); 9      Class<?> serializationView = null; 10      Object value = object; 11      if (value instanceof MappingJacksonValue) { 12         MappingJacksonValue container = (MappingJacksonValue) object; 13         value = container.getValue(); 14         serializationView = container.getSerializationView(); 15      } 16      if (serializationView != null) { 17         this.objectMapper.writerWithView(serializationView).writeValue(generator, value); 18      } 19      else { 20         this.objectMapper.writeValue(generator, value); 21      } 22      writeSuffix(generator, object); 23      generator.flush(); 24 25   } 26   catch (JsonProcessingException ex) { 27      throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex); 28   } 29} 30 31@Override 32protected void writePrefix(JsonGenerator generator, Object object) throws IOException { 33   if (this.jsonPrefix != null) { 34      generator.writeRaw(this.jsonPrefix); 35   } 36   String jsonpFunction = 37         (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null); 38   if (jsonpFunction != null) { 39      generator.writeRaw(jsonpFunction + "("); 40   } 41} 42 43@Override 44protected void writeSuffix(JsonGenerator generator, Object object) throws IOException { 45   String jsonpFunction = 46         (object instanceof MappingJacksonValue ? ((MappingJacksonValue) object).getJsonpFunction() : null); 47   if (jsonpFunction != null) { 48      generator.writeRaw(");"); 49   } 50}

代码非常清晰。看我们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"});

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )